语境:
二进制协议定义给定大小的帧是很常见的。该struct模块擅长解析它,前提是所有内容都已在单个缓冲区中接收到。
问题:
TCP 套接字是流。从套接字读取不能提供比请求更多的字节,但可以返回更少。所以这段代码不可靠:
def readnbytes(sock, n):
return sock.recv(n) # can return less than n bytes
Run Code Online (Sandbox Code Playgroud)
天真的解决方法:
def readnbytes(sock, n):
buff = b''
while n > 0:
b = sock.recv(n)
buff += b
if len(b) == 0:
raise EOFError # peer socket has received a SH_WR shutdown
n -= len(b)
return buff
Run Code Online (Sandbox Code Playgroud)
可能效率不高,因为如果我们请求大量字节,并且数据非常碎片化,我们将反复重新分配一个新的字节缓冲区。
题:
如何在没有重新分配风险的情况下从流套接字可靠地接收 n 个字节?
参考:
那些其他问题是相关的,并且确实提供了提示,但没有一个给出简单而明确的答案:
解决方案是使用recv_into和一个memoryview. Python 允许预先分配一个bytearray可以传递给recv_into. 但是您不能将数据接收到字节数组的切片中,因为该切片将是一个副本。但是 amemoryview允许将多个片段接收到相同的bytearray:
def readnbyte(sock, n):
buff = bytearray(n)
pos = 0
while pos < n:
cr = sock.recv_into(memoryview(buff)[pos:])
if cr == 0:
raise EOFError
pos += cr
return buff
Run Code Online (Sandbox Code Playgroud)
您可以使用socket.makefile()将套接字包装在类似文件的对象中。然后,读取将准确返回请求的数量,除非套接字已关闭,可以返回剩余部分。这是一个例子:
服务器.py
from socket import *
sock = socket()
sock.bind(('',5000))
sock.listen(1)
with sock:
client,addr = sock.accept()
with client, client.makefile() as clientfile:
while True:
data = clientfile.read(5)
if not data: break
print(data)
Run Code Online (Sandbox Code Playgroud)
客户端.py
from socket import *
import time
sock = socket()
sock.connect(('localhost',5000))
with sock:
sock.sendall(b'123')
time.sleep(.5)
sock.sendall(b'451234')
time.sleep(.5)
sock.sendall(b'51234')
Run Code Online (Sandbox Code Playgroud)
服务器输出
Run Code Online (Sandbox Code Playgroud)12345 12345 1234
| 归档时间: |
|
| 查看次数: |
1858 次 |
| 最近记录: |