用Python接收图像

时间:2013-06-06 19:02:36

标签: python sockets tcp

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

任何人都可以帮我编辑此代码或创建类似的代码,以便能够接收图像而不是字符串吗?

1 个答案:

答案 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)