我正在使用python 3.x和套接字模块。服务器正在ipv4地址上运行并使用tcp。 我阅读了一些有关如何发送和接收数据的教程。为了使服务器或客户端确保已发送完整的消息,您只需检查发送的数据量是否等于消息的大小即可:
def mysend(self, msg):
totalsent = 0
while totalsent < MSGLEN:
sent = self.sock.send(msg[totalsent:])
if sent == 0:
raise RuntimeError("socket connection broken")
totalsent = totalsent + sent
来源:https://docs.python.org/3/howto/sockets.html#socket-howto
为使客户端确保已收到整个响应,this tutorial建议在响应的开头添加响应的大小。
我的问题:
编辑:
sock.recv(1024)
并且消息的大小只有500到1000个字符时,是否不能确保我会收到所有消息?答案 0 :(得分:1)
首先,要发送所有字节,您不需要循环,因为python套接字提供了一种简单的方法:socket.sendall()
。
现在您的问题是
是的,即使只接收4个字节,您也应该有一个接收循环,该循环在套接字上调用recv()
,直到读取4个字节为止。
您可以,如果,您可以保证这些字符不会出现在消息本身中。但是,您仍然需要在阅读的每个字符中搜索魔术定界符,因此仅给消息正文加上长度是不合适的。
当您致电recv(n)
时,只能保证最多返回 个n字节,而不完全是n个字节。
这里有三种不同的recvall()
方法进行比较:
def recvall(sock, size):
received_chunks = []
buf_size = 4096
remaining = size
while remaining > 0:
received = sock.recv(min(remaining, buf_size))
if not received:
raise Exception('unexpected EOF')
received_chunks.append(received)
remaining -= len(received)
return b''.join(received_chunks)
更短
def recvall2(sock, size):
return sock.recv(size, socket.MSG_WAITALL)
最后是另一个版本,该版本比第一个版本短一些,但缺少几个功能:
def recvall3(sock, size):
result = b''
remaining = size
while remaining > 0:
data = sock.recv(remaining)
result += data
remaining -= len(data)
return result
第二个不错,很简短,但是它依赖于套接字选项socket.MSG_WAITALL
,我不保证每个选项都可以存在。第一个和第三个应该在任何地方工作。我还没有真正建立基准来进行比较和对比。
答案 1 :(得分:1)
对于发送,仅在将套接字置于非阻塞模式时才真正需要该循环。如果套接字处于阻止模式(默认),则sock.send()
直到发送完整个消息或收到错误后才会返回。
但是,由于TCP在协议中不包含消息边界,因此接收没有等效条件。只要有任何数据,sock.recv()
就会返回。
sock.recv()
,直到获得所需的一切为止。与发送例程在每次迭代中发送较短的子字符串的方式类似,您可以将recv()
参数的大小减少到目前为止已读取的数量。看起来像这样:def myrecv(self, size):
buffer = ''
while size > 0:
msg = self.sock.recv(size)
buffer += msg
size -= len(msg)
return buffer
如果在每条消息前放置4个字节的长度,则可以执行以下操作:
msgsize = int(myrecv(4))
message = myrecv(msgsize)
您可以这样做,但这会使事情变得更加复杂。您需要一次读取一个字符,检查定界符,或者实现一个缓冲区来保存您已读取但尚未返回给调用方的数据,因为它已经超出了当前消息的末尾。另外,如果数据可以包含定界符,则需要能够对其进行转义。
否,recv(1024)
可以在收到任何数据后立即返回,该数据可能小于发送的消息的大小。如果保证返回1024个字符,则发件人只发送了500个字符,它将挂起,因为它正在等待剩余的524个字符。