0

以下代码适用于可以接收字符串的 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()

谁能帮我编辑此代码或创建一个类似的代码以便能够接收图像而不是字符串?

4

1 回答 1

5

首先,您的代码实际上无法接收字符串。套接字是字节流,而不是消息流

这一行:

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)

于 2013-06-06T19:33:02.047 回答