以下代码适用于可以接收字符串的python服务器。
import socket
TCP_IP = '127.0.0.1'
TCP_PORT = 8001
BUFFER_SIZE = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connection address:', addr
while 1:
length = conn.recv(1027)
data = conn.recv(int(length))
import StringIO
buff = StringIO.StringIO()
buff.write(data)
if not data: break
print "received data:", data
conn.send('Thanks') # echo
get_result(buff)
conn.close()
任何人都可以帮我编辑此代码或创建类似的代码,以便能够接收图像而不是字符串吗?
答案 0 :(得分:5)
首先,您的代码实际上无法接收字符串。 Sockets are byte streams, not message streams
这一行:
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
匹配。但是,如果您在同一台计算机上运行双方,发送相对较小的缓冲区,并且没有严重加载计算机,则它们通常会碰巧匹配1对1。毕竟,每次你调用recv
时,另一方可能只有send
个消息的时间,这个消息本身就位于操作系统的缓冲区中,所以操作系统只给你一个整体的东西。因此,您的代码似乎可以在初始测试中使用。
但是,如果您通过路由器将消息发送到另一台计算机,或者您等待的时间足以让另一方进行多次send
呼叫,或者您的消息太大而无法放入单个缓冲区,或者如果你只是运气不好,缓冲区中可能会有2-1 / 2条消息等待,操作系统会给你全部的2-1 / 2消息。然后你的下一个recv
将获得剩余的1/2消息。
那么,你如何使图像工作?嗯,这取决于你的意思。
您可以将图像文件作为字节序列读入内存,并在该序列上调用send_string
,它将正常工作。然后另一方可以保存该文件,或将其解释为图像文件并显示它或任何它想要的。
或者,您可以使用PIL
之类的内容将图像文件解析并解压缩为位图。然后,您以某种方式(例如,pickle
),send_string
标题,然后send_string
位图编码标题数据(宽度,高度,像素格式等)。 / p>
如果标题具有固定大小(例如,它是一个可以使用struct.pack
序列化的简单结构),并且包含足够的信息以便另一方以字节为单位计算出位图的长度,那么每个人都需要send_string
;只需使用conn.sendall(serialized_header)
然后使用conn.sendall(bitmap)
。