数据包有时发送完整有时不发送完整
packet is sent completely somtimes and somtimes is not sent completely
@Grismar 建议我为以下问题创建新主题:
我用 socket
module.For 多连接写了一个服务器和客户端 我用 selectors module 而不是 thread
或 fork()
.
场景:我必须生成大量字符串并根据客户端生成的字符串发送到 client.Of 课程。实际上,客户端发送查询,服务器生成结果并发送给客户端。我没有向服务器发送查询的问题。
因为我有大量的字符串,所以我决定将我的字符串分成块,例如:
if sys.getsizeof(search_result_string) > 1024: #131072:
if sys.getsizeof(search_result_string) % 1024 == 0:
chunks = int(sys.getsizeof(search_result_string) / 1024 )
else:
chunks = int(sys.getsizeof(search_result_string) / 1024) + 1
for chunk in range(chunks):
packets.append(search_result_string[:1024])
search_result_string = search_result_string[1024:]
所以,我有数据包列表。
那么:
conn.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1000000)
for chunk in packets:
conn.sendall(bytes(chunk,'utf-8'))
有时我在客户端没有任何问题,有时我收到以下错误:
Traceback (most recent call last):
File "./multiconn-client.py", line 116, in <module>
service_connection(key, mask)
File "./multiconn-client.py", line 89, in service_connection
target_string += recv_data.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd9 in position 42242: unexpected end of data
在我的客户端,我使用了以下回调:
def service_connection(key, mask):
buff = 10000
sock = key.fileobj
data = key.data
target_string = str()
if mask & selectors.EVENT_READ:
buff = sock.getsockopt(SOL_SOCKET,SO_RCVBUF)
recv_data = sock.recv( 128*1024 |buff)
if recv_data:
buff = sock.getsockopt(SOL_SOCKET,SO_RCVBUF)
data.recv_total += len(recv_data)
target_string += recv_data.decode('utf-8')
print(target_string)
if not recv_data: #or data.recv_total == data.msg_total:
print("closing connection", data.connid)
sel.unregister(sock)
sock.close()
if mask & selectors.EVENT_WRITE:
if not data.outb and data.messages:
data.outb = data.messages.pop(0)
if data.outb:
print("sending", repr(data.outb), "to connection", data.connid)
sent = sock.send(data.outb) # Should be ready to write
data.outb = data.outb[sent:]
顺便说一句,我在本地主机都使用 TCP socket.And 测试。
我对每个 运行 使用相同的字符串。
问题是,为什么有时一切正常,有时字符串发送不完整。
发生的事情是你的数据被操作系统分块(除了你正在做的事情)。当操作系统执行此操作时,它可能会在 UTF-8 编码序列的中间拆分您的数据。换句话说,考虑这个代码块:
foo = '\xce\xdd\xff' # three non-ascii characters
print(len(foo)) # => 3
bar = foo.encode('utf-8')
print(bar) # => b'\xc3\x8e\xc3\x9d\xc3\xbf'
bar[:3].decode() # =>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 2: unexpected end of data
发生了什么:0x7f 以上的字符被编码为两个 UTF8 字节。但是,如果两字节序列在中间被截断,则无法解码字符。
因此,要轻松解决您的问题,请先接收所有数据(作为字节串),然后将整个字节串作为一个单元进行解码。
这带来了另一个相关问题:您不需要创建自己的数据块。 TCP 会为你做到这一点。正如您所见,TCP 无论如何都不会 保留 您的消息边界。所以你最好的选择是正确地 "frame" 你的数据。
也就是说,提取字符串的一部分(如果不是数百兆字节,则提取全部字符串),然后将其编码为 UTF-8。获取生成的字节缓冲区的长度。作为二进制数据发送包含该长度的固定长度大小字段(使用 struct
模块创建)。在接收端,首先接收固定长度的大小字段。这让您知道实际需要接收多少字节的字符串数据。接收所有这些字节,然后立即解码整个缓冲区。
也就是说,忽略错误处理,发送端:
import struct
import socket
...
str_to_send = "blah blah\xce"
bytes_to_send = str_to_send.encode('utf-8')
len_bytes = len(bytes_to_send)
sock.send(struct.pack("!I", len_bytes) # Send 4-byte size header
sock.send(bytes_to_send) # Let TCP handle chunking bytes
接收方:
len_bytes = sock.recv(4) # Receive 4-byte size header
len_bytes = struct.unpack("!I")[0] # Convert to number (unpack returns a list)
bytes_sent = b''
while len(bytes_sent) < len_bytes:
buf = sock.recv(1024) # Note, may not always receive 1024 (but typically will)
if not buf:
print("Unexpected EOF!")
sys.exit(1)
bytes_sent += buf
str_sent = bytes_sent.decode('utf-8')
结语:socket.send
不 保证发送整个缓冲区(尽管它通常会发送)。并且 socket.recv
不保证接收您在参数中指定的字节数。因此,健壮的 TCP sending/receiving 代码需要适应这些警告。
@Grismar 建议我为以下问题创建新主题:
我用 socket
module.For 多连接写了一个服务器和客户端 我用 selectors module 而不是 thread
或 fork()
.
场景:我必须生成大量字符串并根据客户端生成的字符串发送到 client.Of 课程。实际上,客户端发送查询,服务器生成结果并发送给客户端。我没有向服务器发送查询的问题。
因为我有大量的字符串,所以我决定将我的字符串分成块,例如:
if sys.getsizeof(search_result_string) > 1024: #131072:
if sys.getsizeof(search_result_string) % 1024 == 0:
chunks = int(sys.getsizeof(search_result_string) / 1024 )
else:
chunks = int(sys.getsizeof(search_result_string) / 1024) + 1
for chunk in range(chunks):
packets.append(search_result_string[:1024])
search_result_string = search_result_string[1024:]
所以,我有数据包列表。 那么:
conn.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1000000)
for chunk in packets:
conn.sendall(bytes(chunk,'utf-8'))
有时我在客户端没有任何问题,有时我收到以下错误:
Traceback (most recent call last):
File "./multiconn-client.py", line 116, in <module>
service_connection(key, mask)
File "./multiconn-client.py", line 89, in service_connection
target_string += recv_data.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd9 in position 42242: unexpected end of data
在我的客户端,我使用了以下回调:
def service_connection(key, mask):
buff = 10000
sock = key.fileobj
data = key.data
target_string = str()
if mask & selectors.EVENT_READ:
buff = sock.getsockopt(SOL_SOCKET,SO_RCVBUF)
recv_data = sock.recv( 128*1024 |buff)
if recv_data:
buff = sock.getsockopt(SOL_SOCKET,SO_RCVBUF)
data.recv_total += len(recv_data)
target_string += recv_data.decode('utf-8')
print(target_string)
if not recv_data: #or data.recv_total == data.msg_total:
print("closing connection", data.connid)
sel.unregister(sock)
sock.close()
if mask & selectors.EVENT_WRITE:
if not data.outb and data.messages:
data.outb = data.messages.pop(0)
if data.outb:
print("sending", repr(data.outb), "to connection", data.connid)
sent = sock.send(data.outb) # Should be ready to write
data.outb = data.outb[sent:]
顺便说一句,我在本地主机都使用 TCP socket.And 测试。
我对每个 运行 使用相同的字符串。
问题是,为什么有时一切正常,有时字符串发送不完整。
发生的事情是你的数据被操作系统分块(除了你正在做的事情)。当操作系统执行此操作时,它可能会在 UTF-8 编码序列的中间拆分您的数据。换句话说,考虑这个代码块:
foo = '\xce\xdd\xff' # three non-ascii characters
print(len(foo)) # => 3
bar = foo.encode('utf-8')
print(bar) # => b'\xc3\x8e\xc3\x9d\xc3\xbf'
bar[:3].decode() # =>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 2: unexpected end of data
发生了什么:0x7f 以上的字符被编码为两个 UTF8 字节。但是,如果两字节序列在中间被截断,则无法解码字符。
因此,要轻松解决您的问题,请先接收所有数据(作为字节串),然后将整个字节串作为一个单元进行解码。
这带来了另一个相关问题:您不需要创建自己的数据块。 TCP 会为你做到这一点。正如您所见,TCP 无论如何都不会 保留 您的消息边界。所以你最好的选择是正确地 "frame" 你的数据。
也就是说,提取字符串的一部分(如果不是数百兆字节,则提取全部字符串),然后将其编码为 UTF-8。获取生成的字节缓冲区的长度。作为二进制数据发送包含该长度的固定长度大小字段(使用 struct
模块创建)。在接收端,首先接收固定长度的大小字段。这让您知道实际需要接收多少字节的字符串数据。接收所有这些字节,然后立即解码整个缓冲区。
也就是说,忽略错误处理,发送端:
import struct
import socket
...
str_to_send = "blah blah\xce"
bytes_to_send = str_to_send.encode('utf-8')
len_bytes = len(bytes_to_send)
sock.send(struct.pack("!I", len_bytes) # Send 4-byte size header
sock.send(bytes_to_send) # Let TCP handle chunking bytes
接收方:
len_bytes = sock.recv(4) # Receive 4-byte size header
len_bytes = struct.unpack("!I")[0] # Convert to number (unpack returns a list)
bytes_sent = b''
while len(bytes_sent) < len_bytes:
buf = sock.recv(1024) # Note, may not always receive 1024 (but typically will)
if not buf:
print("Unexpected EOF!")
sys.exit(1)
bytes_sent += buf
str_sent = bytes_sent.decode('utf-8')
结语:socket.send
不 保证发送整个缓冲区(尽管它通常会发送)。并且 socket.recv
不保证接收您在参数中指定的字节数。因此,健壮的 TCP sending/receiving 代码需要适应这些警告。