Python Socket接收大量数据

时间:2013-07-16 04:16:14

标签: python sockets

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

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()

12 个答案:

答案 0 :(得分:100)

TCP / IP是基于流的协议,而不是基于消息的协议。无法保证一个对等方的每次send()调用都会导致另一个对等方发出一个recv()调用,这个对等方接收到发送的确切数据 - 它可能会收到数据分餐,分为多个{{1}调用,由于数据包碎片。

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

发送消息的一种简单方法是为每条消息添加长度前缀。然后要读取消息,首先读取长度,然后读取那么多字节。以下是您可以这样做的方式:

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_msg函数发送和接收整个邮件,并且在网络级别拆分或合并数据包时不会有任何问题。

答案 1 :(得分:18)

您可以将其用作: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

答案 2 :(得分:6)

接受的答案很好但是大文件会很慢-string是一个不可变类,这意味着每次使用@Consumes(value={"application/vnd.api+json",MediaType.APPLICATION_JSON})符号时都会创建更多对象,使用+作为堆栈结构将更有效。

这应该效果更好

list

答案 3 :(得分:4)

您可能需要多次调用conn.recv()才能接收所有数据。由于TCP流不保持帧边界(即它们仅作为原始字节流而不是结构化消息流),因此无法保证一次调用它会引入所有已发送的数据。

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

请注意,这意味着您需要某种方式来了解何时收到所有数据。如果发送方总是发送8000字节,您可以计算到目前为止收到的字节数,并从8000中减去这些字节,以了解剩余的字节数。如果数据是可变大小的,则可以使用各种其他方法,例如让发送方在发送消息之前发送一个字节数的标头,或者如果它是正在发送的ASCII文本,您可以查找换行符或NUL字符。

答案 4 :(得分:3)

使用生成器函数的变体(我认为更加pythonic):

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))

答案 5 :(得分:3)

免责声明:在极少数情况下,您确实需要执行此操作。如果可能,请使用现有的应用程序层协议或定义自己的协议。在每条消息之前加上一个固定长度的整数,该整数指示在每个消息之后或以'\ n'字符终止的数据的长度。 (亚当·罗森菲尔德(Adam Rosenfield)的answer在解释这一点上做得非常好)

话虽如此,但有一种方法可以读取套接字上的所有可用数据。但是,依靠这种通信是一个坏主意,因为它会带来丢失数据的风险。仅在阅读以下说明后,请格外谨慎地使用此解决方案。

def recvall(sock):
    BUFF_SIZE = 4096
    data = bytearray()
    while True:
        packet = sock.recv(BUFF_SIZE)
        if not packet:  # Important!!
            break
        data.extend(packet)
    return data

现在if not packet:行绝对重要! 此处的许多答案建议使用类似 {if len(packet) < BUFF_SIZE: 这样的条件,该条件已损坏,很可能会导致您过早关闭连接并丢失数据。错误地假设在TCP套接字的一端发送的一个消息对应于在另一端发送的多个字节的接收消息。它不是。 很有可能sock.recv(BUFF_SIZE)会返回一个小于BUFF_SIZE的块,即使仍有等待接收的数据。问题{{ 3}}和here

使用上述解决方案如果连接的另一端写入数据的速度比读取速度慢,您仍然有数据丢失的风险。您可能只是简单地消耗掉了所有数据,而在有更多数据时退出。有很多方法需要使用并发编程,但这是它自己的另一个主题。

答案 6 :(得分:1)

修改Adam Rosenfield的代码:

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
但是,我会大力鼓励使用原始方法。

答案 7 :(得分:0)

您可以使用序列化完成

from socket import *
from json import dumps, loads

def recvall(conn):
    data = ""
    while True:
    try:
        data = conn.recv(1024)
        return json.loads(data)
    except ValueError:
        continue

def sendall(conn):
    conn.sendall(json.dumps(data))

注意:如果要使用上面的代码存储文件,则需要将其编码/解码为base64

答案 8 :(得分:0)

大多数答案描述了某种recvall()方法。如果您在接收数据时遇到的瓶颈是在for循环中创建字节数组,那么我在recvall()方法中对三种分配接收数据的方法进行了基准测试:

字节字符串方法:

arr = b''
while len(arr) < msg_len:
    arr += sock.recv(max_msg_size)

列表方法:

fragments = []
while True: 
    chunk = sock.recv(max_msg_size)
    if not chunk: 
        break
    fragments.append(chunk)
arr = b''.join(fragments)

预分配的bytearray方法:

arr = bytearray(msg_len)
pos = 0
while pos < msg_len:
    arr[pos:pos+max_msg_size] = sock.recv(max_msg_size)
    pos += max_msg_size

结果:

enter image description here

答案 9 :(得分:0)

在您不知道数据包长度的情况下,其他正在寻找答案的人。 这是一个简单的解决方案,一次读取4096个字节,并在收到少于4096个字节时停止。但是,在接收到的数据包的总长度恰好是4096个字节的情况下,它将不起作用-然后它将再次调用recv()并挂起。

def recvall(sock):
    data = b''
    bufsize = 4096
    while True:
        packet = sock.recv(bufsize)
        data += packet
        if len(packet) < bufsize:
            break
    return data

答案 10 :(得分:0)

我认为这个问题已经很好地回答了,但是我只是想使用Python 3.8和新的赋值表达式(海象运算符)添加一个方法,因为它在风格上很简单。

import socket

host = "127.0.0.1"
port = 31337
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host,port))
s.listen()
con, addr = s.accept()
msg_list = []

while (walrus_msg := con.recv(3)) != b'\r\n':
    msg_list.append(walrus_msg)

print(msg_list)

在这种情况下,从套接字接收到3个字节,并立即将它们分配给walrus_msg。一旦套接字收到b'\r\n',它就会中断循环。将walrus_msg添加到msg_list并在循环中断后打印。该脚本是基本脚本,但已经过测试,可用于telnet会话。

注意:(walrus_msg := con.recv(3))的圆括号是必需的。没有这个,while walrus_msg := con.recv(3) != b'\r\n':会将walrus_msg评估为True,而不是套接字上的实际数据。

答案 11 :(得分:0)

这段代码在 32 次迭代中从 socket 编程-python 中从服务器接收的缓冲区中读取 1024*32(=32768) 个字节:

jsonString = bytearray()

for _ in range(32):

    packet = clisocket.recv(1024)
    if not packet:
       break
    jsonString.extend(packet)

数据驻留在 jsonString 变量中