2013-07-16 64 views
26

当我尝试接收大量数据时,它会被截断,我必须按Enter才能获取其余数据。起初我能够增加一点,但它仍然不会收到全部。正如你所看到的,我增加了conn.recv()上的缓冲区,但它仍然没有获得所有的数据。它在某个时刻切断它。我必须在我的raw_input上按回车才能接收其余的数据。无论如何,我可以一次获得所有的数据吗?这是代码。Python Socket接收大量数据

port = 7777 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
sock.bind(('0.0.0.0', port)) 
sock.listen(1) 
print ("Listening on port: "+str(port)) 
while 1: 
    conn, sock_addr = sock.accept() 
    print "accepted connection from", sock_addr 
    while 1: 
     command = raw_input('shell> ') 
      conn.send(command) 
       data = conn.recv(8000) 
       if not data: break 
       print data, 
    conn.close() 

回答

67

TCP/IP是基于流的协议,而不是基于消息的协议。无法保证每一个对方的呼叫都会导致一个recv()呼叫由另一个对等方接收到发送的确切数据 - 由于数据包分段,它可能会收到数据零食,分割多个recv()呼叫。

您需要在TCP之上定义自己的基于消息的协议,以便区分消息边界。然后,要阅读邮件,请继续拨打recv(),直到您阅读完整邮件或发生错误。

发送消息的一种简单方法是以每个消息的长度作为前缀。然后读取一条消息,首先读取长度,然后读取那么多字节。这里是你将如何做到这一点:

def send_msg(sock, msg): 
    # Prefix each message with a 4-byte length (network byte order) 
    msg = struct.pack('>I', len(msg)) + msg 
    sock.sendall(msg) 

def recv_msg(sock): 
    # Read message length and unpack it into an integer 
    raw_msglen = recvall(sock, 4) 
    if not raw_msglen: 
     return None 
    msglen = struct.unpack('>I', raw_msglen)[0] 
    # Read the message data 
    return recvall(sock, msglen) 

def recvall(sock, n): 
    # Helper function to recv n bytes or return None if EOF is hit 
    data = b'' 
    while len(data) < n: 
     packet = sock.recv(n - len(data)) 
     if not packet: 
      return None 
     data += packet 
    return data 

然后你可以使用send_msgrecv_msg功能来发送和接收整个邮件,并且不会与包被拆分或合并在网络级别的任何问题。

+0

真棒感谢这么多。我很感激。 – user2585107

+0

我不确定我是否完全理解这一点。我明白应该发生什么,但我似乎无法得到它。我越来越'例外:套接字EOF试图recv 4个字节' 我使用以下内容:http://pastebin.com/raw.php?i=AvdN5RyW – user2585107

+0

@ user2585107:尝试更新的版本,它使用'返回None'而不是在流结束时引发异常。 –

3

您可能需要调用conn.recv()多次接收的所有数据。由于TCP流不保留帧边界(即,它们只能用作原始字节流,而不是结构化消息流),所以不能保证一次调用所有数据, 。

有关该问题的其他说明,请参见this answer

请注意,这意味着您需要知道何时收到所有数据。如果发送方总是发送8000个字节,则可以计算迄今为止收到的字节数,并从8000中减去该字节数以知道还有多少字节可以接收;如果数据是可变大小的,还可以使用其他各种方法,例如让发送者在发送消息之前发送多个字节头,或者如果它正在发送的ASCII文本可以查找换行符或NUL字符。

10

你可以使用它作为:data = recvall(sock)

def recvall(sock): 
    BUFF_SIZE = 4096 # 4 KiB 
    data = b'' 
    while True: 
     part = sock.recv(BUFF_SIZE) 
     data += part 
     if len(part) < BUFF_SIZE: 
      # either 0 or end of data 
      break 
    return data 
+3

这适用于检测“文件结束”,但不适用于保持连接并检测消息的结尾。只有在peeer关闭其部分插槽或至少将其关闭一半时才能达到“文件结束”。 – glglgl

+3

如果收到的字符串少于4096个字符,它将再次循环,并使用'sock.recv()'重新检查更多数据。这会挂起,因为没有更多的数据进入。如果'part'的长度小于'RECV_BUFFER'的长度,那么代码可以安全地跳出循环。 – SomeGuyOnAComputer

+1

@SomeGuyOnAComputer,谢谢,修正。 – JadedTuna

0

修改亚当罗森菲尔德代码:

import sys 


def send_msg(sock, msg): 
    size_of_package = sys.getsizeof(msg) 
    package = str(size_of_package)+":"+ msg #Create our package size,":",message 
    sock.sendall(package) 

def recv_msg(sock): 
    try: 
     header = sock.recv(2)#Magic, small number to begin with. 
     while ":" not in header: 
      header += sock.recv(2) #Keep looping, picking up two bytes each time 

     size_of_package, separator, message_fragment = header.partition(":") 
     message = sock.recv(int(size_of_package)) 
     full_message = message_fragment + message 
     return full_message 

    except OverflowError: 
     return "OverflowError." 
    except: 
     print "Unexpected error:", sys.exc_info()[0] 
     raise 

我会,但是,大量使用鼓励的原始方法。

3

使用生成功能(我认为更Python)的变化:

def recvall(sock, buffer_size=4096): 
    buf = sock.recv(buffer_size) 
    while buf: 
     yield buf 
     if len(buf) < buffer_size: break 
     buf = sock.recv(buffer_size) 
# ... 
with socket.create_connection((host, port)) as sock: 
    sock.sendall(command) 
    response = b''.join(recvall(sock)) 
+0

的端那一个不会出现工作,如果该响应是大于缓冲区的大小要小。 – Shadur

+0

@Shadur,这很有趣,当你尝试时会发生什么?你能分享一下代码来重现这个问题吗?正如所写,'recvall'应该产生接收到的每个缓冲区的内容,而不管它的大小如何,只要它不是空的。 – yoniLavi

+1

通过添加的调试语句判断,它吸入第一个块中的整个响应,然后挂起,等待下一个块。下面的'chunck'答案也有同样的问题,我在第二次测试中修复了它,看看chunck的长度是否小于缓冲区大小。我会测试是否可以修复您的解决方案。 - 编辑:它。 – Shadur

3

接受的答案是好的,但它会随着大文件-string很慢是不可变类,这意味着更多的对象是创建每次使用+符号时,使用list作为堆栈结构会更有效率。

这应该更好地工作

while True: 
    chunck = s.recv(10000) 
    if not chunck: 
     break 
    fragments.append(chunck) 

print "".join(fragments) 
相关问题