首先,您的代码实际上无法接收字符串。套接字是字节流,而不是消息流。
这一行:
length = conn.recv(1027)
… 将接收 1 到 1027 个字节。
您需要围绕每个循环recv
并累积一个缓冲区,如下所示:
def recvall(conn, length):
buf = b''
while len(buf) < length:
data = conn.recv(length - len(buf))
if not data:
return data
buf += data
return buf
现在你可以让它像这样工作:
while True:
length = recvall(conn, 1027)
if not length: break
data = recvall(conn, int(length))
if not data: break
print "received data:", data
conn.send('Thanks') # echo
出于性能原因,您可以使用StringIO
或其他技术代替串联,但我忽略了它,因为这种方式更简单、更简洁,并且理解代码比性能更重要。
同时,值得指出的是,1027 字节对于长度前缀来说是一个荒谬的巨大空间。此外,无论如何,您的发送代码必须确保实际发送 1027 个字节。并且您的响应必须始终为 6 个字节长才能正常工作。
def send_string(conn, msg):
conn.sendall(str(len(msg)).ljust(1027))
conn.sendall(msg)
response = recvall(conn, 6)
return response
但至少现在它是可行的。
那么,为什么你认为它有效?
TCP 是字节流,而不是消息流。不能保证一侧的单曲send
与另一侧的下一曲相匹配recv
。但是,当您在同一台计算机上同时运行双方,发送相对较小的缓冲区,并且不会使计算机负载过重时,它们通常会发生一对一的匹配。毕竟,每次您调用recv
时,对方可能只有时间接收send
一条消息,该消息完全位于操作系统的缓冲区中,因此操作系统只会为您提供全部信息。因此,您的代码在初始测试中似乎可以工作。
但是,如果您通过路由器将消息发送到另一台计算机,或者如果您等待足够长的时间让对方拨打多个send
电话,或者如果您的消息太大而无法放入单个缓冲区,或者如果您运气不好,那么可能是 2-1/2 消息在缓冲区中等待,操作系统会给你整个 2-1/2 消息。然后你的下一个recv
将得到剩余的 1/2 消息。
那么,您如何使这项工作适用于图像?嗯,这取决于你的意思。
您可以将图像文件作为字节序列读入内存,然后调用send_string
该序列,它将正常工作。然后对方可以保存该文件,或将其解释为图像文件并显示它,或任何它想要的。
或者,您可以使用类似PIL
的方法将图像文件解析并解压缩为位图。pickle
然后,您以某种方式(例如,它)对标题数据(宽度、高度、像素格式等) 、send_string
标题send_string
和位图进行编码。
如果标头具有固定大小(例如,它是一个可以用 序列化的简单结构struct.pack
),并且包含足够的信息以供另一方计算位图的长度(以字节为单位),则不需要send_string
每一个;就用conn.sendall(serialized_header)
then conn.sendall(bitmap)
。