在Python中通过套接字发送大量数据的正确方法是什么? [英] What is the proper way of sending a large amount of data over sockets in Python?

查看:233
本文介绍了在Python中通过套接字发送大量数据的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我写了一些代码(客户端和服务器)来发送图像 - 客户端只需将图像上传到服务器,只需使用套接字模块:



在我的服务器代码(接收图像)中,我有以下几行:

  myfile = open(basename%imgcounter, 'wb')
myfile.write(data)

data = sock.recv(40960000)
如果不是数据:
myfile.close()
break
myfile.write(data)
myfile.close()

sock.sendall(GOT IMAGE)
sock.shutdown()

但我不认为这是最好的方法。我想我应该实现服务器,以便它以块的形式接收数据:

 #!/ usr / bin / env python 

导入随机
导入套接字,从导入时间选择
gmtime,strftime
从随机导入randint

imgcounter = 1
basename =image%s.png

HOST ='127.0.0.1'
PORT = 2905

connected_clients_sockets = []

server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server_socket.bind((HOST,PORT) ))
server_socket.listen(10)

connected_clients_sockets.append(server_socket)

而True:

read_sockets,write_sockets,error_sockets = select.select(connected_clients_sockets,[],[])

用于read_sockets中的sock:

如果sock == server_socket:

sockfd, client_address = server_socket.accept()
co nnected_clients_sockets.append(sockfd)

else:
try:

data = sock.recv(4096)
txt = str(data)

如果数据:

如果data.startswith('SIZE'):
tmp = txt.split()
size = int(tmp [1] )

print'get size%s'%size

sock.sendall(GOT SIZE)

elif data.startswith('BYE '):
sock.shutdown()

else:

myfile = open(basename%imgcounter,'wb')
myfile.write(数据)

amount_received = 0
而amount_received<大小:
data = sock.recv(4096)
amount_received + = len(数据)

打印amount_received

如果不是数据:
break
myfile.write(data)
myfile.close()

sock.sendall(GOT IMAGE)
sock.shutdown()
除外:
sock.close()
connected_clients_sockets.remove(sock)
继续
imgcounter + = 1
server_socket.close()

但是当我这样做时,服务器打印出来:

 大小54674 
4096
8192
12288
16384
20480
24576
28672
32768
36864
40960
45056
49152
50578

然后似乎挂了,而且客户也挂了。但是,在服务器端,我只能看到客户想要发送的图像的一部分:





似乎缺少一些字节。使用ONLY套接字发送大量数据(图像,其他类型的文件)的正确方法是什么?

解决方案

我我假设你有一个特殊的理由用赤裸的插座来做这件事,比如自我修饰,这意味着我不会回答说你不小心忘了使用HTTP和Twisted,这可能你已经< a href =https://stackoverflow.com/questions/42356322/tcp-client-server-with-sockets-server-sending-files-to-clients-client-hangs-p/42448335>听说过 :-P。但实际上你应该在某些时候看看更高级别的库,因为它们更容易!



定义协议



如果您想要的只是发送图像,那么它可以很简单:


  1. 客户端 - >服务器:8字节:大端,图像长度。

  2. 客户端 - > server:length bytes :所有图像数据。

  3. Client< - server:1 byte,value 0 :表示收到传输 - 可选步骤你可能不在乎你是否使用TCP而只是假设它是可靠的。)



编码



server.py

  import os 
来自套接字导入*
来自struct import unpack


class ServerProtocol:

def __init __(self):
self.socket = None
self.output_dir ='。'
self.file_num = 1

def listen(self,server_ip,server_port):
self.socket = socket(AF_INET, SOCK_STREAM)
self.socket.bind((server_ip,server_port))
self.socket.listen(1)

def handle_images(self):

try:
而True:
(连接,addr)= self.socket.accept()
尝试:
bs =连接.recv(8)
(长度,)=解包('> Q',bs)
data = b''
而len(数据)<长度:
#批量执行#一般比尝试
#更好,所以我相信。
to_read = length - len(数据)
data + = connection.recv(
4096如果to_read> 4096其他to_read)

#发送我们的0 ack
断言len(b'\00')== 1
connection.sendall(b'\00')
最后:
connection.shutdown(SHUT_WR)
connection.close()

with open(os.path.join(
self.output_dir,'%06d.jpg'%self.file_num),'w'
)as fp:
fp.write(data)

self.file_num + = 1
finally:
self.close()

def close(self):
self.socket.close()
self.socket =无

#可以在这里处理一个糟糕的确认,但我们会认为它很好。

如果__name__ =='__ main__':
sp = ServerProtocol()
sp.listen('127.0.0.1',55555)
sp.handle_images()

client.py

来自套接字导入的 *来自struct import pack的



class ClientProtocol:

def __init __(self):
self.socket =无

def connect(self,server_ip,server_port):
self.socket = socket(AF_INET,SOCK_STREAM)
self.socket.connect((server_ip) ,server_port))

def close(self):
self.socket.shutdown(SHUT_WR)
self.socket.close()
self.socket = None

def send_image(self,image_data):

#使用struct确保我们在长度上有一致的字节顺序
length = pack('> Q ',len(image_data))

#sendall以确保它在套接字上有背压时阻止
self.socket.s endall(length)
self.socket.sendall(image_data)

ack = self.socket.recv(1)

#可以在这里处理错误的ack,但我们会认为这很好。

if __name__ =='__ main__':
cp = ClientProtocol()

image_data =无
with open('IMG_0077.jpg',' r')为fp:
image_data = fp.read()

断言(len(image_data))
cp.connect('127.0.0.1',55555)
cp.send_image(image_data)
cp.close()


Recently I wrote some code (client and server) to send an image - the client simply uploads the image to the server, just using the socket module: Sending image over sockets (ONLY) in Python, image can not be open.

However, the image sending part is now what I am concerned with. This is the original image I'm using:

In my server code (which receives the images), I have these lines:

myfile = open(basename % imgcounter, 'wb')
myfile.write(data)

data = sock.recv(40960000)
if not data:
     myfile.close()
     break
myfile.write(data)
myfile.close()

sock.sendall("GOT IMAGE")
sock.shutdown()

But I don't think this is the best way of doing it. I think I should instead implement the server such that it receives the data in chunks:

#!/usr/bin/env python

import random
import socket, select
from time import gmtime, strftime
from random import randint

imgcounter = 1
basename = "image%s.png"

HOST = '127.0.0.1'
PORT = 2905

connected_clients_sockets = []

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen(10)

connected_clients_sockets.append(server_socket)

while True:

    read_sockets, write_sockets, error_sockets = select.select(connected_clients_sockets, [], [])

    for sock in read_sockets:

        if sock == server_socket:

            sockfd, client_address = server_socket.accept()
            connected_clients_sockets.append(sockfd)

        else:
            try:

                data = sock.recv(4096)
                txt = str(data)

                if data:

                    if data.startswith('SIZE'):
                        tmp = txt.split()
                        size = int(tmp[1])

                        print 'got size %s' % size

                        sock.sendall("GOT SIZE")

                    elif data.startswith('BYE'):
                        sock.shutdown()

                    else :

                        myfile = open(basename % imgcounter, 'wb')
                        myfile.write(data)

                        amount_received = 0
                        while amount_received < size:
                            data = sock.recv(4096)
                            amount_received += len(data)

                            print amount_received

                            if not data:
                                break
                            myfile.write(data)
                        myfile.close()

                        sock.sendall("GOT IMAGE")
                        sock.shutdown()
            except:
                sock.close()
                connected_clients_sockets.remove(sock)
                continue
        imgcounter += 1
server_socket.close()

But when I do this, the server prints:

got size 54674
4096
8192
12288
16384
20480
24576
28672
32768
36864
40960
45056
49152
50578

And then seems to hang, and the client hangs too. However, at the server's side I can see only a piece of the image the client wanted to send:

It seems like there are some bytes missing. What is the proper way of sending a huge amount of data (images, other type of file) using ONLY sockets?

解决方案

I'm assuming that you have a particular reason for doing this with naked sockets, such as self-edification, which means that I won't answer by saying "You accidentally forgot to just use HTTP and Twisted", which perhaps you've heard before :-P. But really you should look at higher-level libraries at some point as they're a lot easier!

Define a protocol

If all you want is to send an image, then it can be simple:

  1. Client -> server: 8 bytes: big endian, length of image.
  2. Client -> server: length bytes: all image data.
  3. (Client <- server: 1 byte, value 0: indicate transmission received - optional step you may not care if you're using TCP and just assume that it's reliable.)

Code it

server.py

import os
from socket import *
from struct import unpack


class ServerProtocol:

    def __init__(self):
        self.socket = None
        self.output_dir = '.'
        self.file_num = 1

    def listen(self, server_ip, server_port):
        self.socket = socket(AF_INET, SOCK_STREAM)
        self.socket.bind((server_ip, server_port))
        self.socket.listen(1)

    def handle_images(self):

        try:
            while True:
                (connection, addr) = self.socket.accept()
                try:
                    bs = connection.recv(8)
                    (length,) = unpack('>Q', bs)
                    data = b''
                    while len(data) < length:
                        # doing it in batches is generally better than trying
                        # to do it all in one go, so I believe.
                        to_read = length - len(data)
                        data += connection.recv(
                            4096 if to_read > 4096 else to_read)

                    # send our 0 ack
                    assert len(b'\00') == 1
                    connection.sendall(b'\00')
                finally:
                    connection.shutdown(SHUT_WR)
                    connection.close()

                with open(os.path.join(
                        self.output_dir, '%06d.jpg' % self.file_num), 'w'
                ) as fp:
                    fp.write(data)

                self.file_num += 1
        finally:
            self.close()

    def close(self):
        self.socket.close()
        self.socket = None

        # could handle a bad ack here, but we'll assume it's fine.

if __name__ == '__main__':
    sp = ServerProtocol()
    sp.listen('127.0.0.1', 55555)
    sp.handle_images()

client.py

from socket import *
from struct import pack


class ClientProtocol:

    def __init__(self):
        self.socket = None

    def connect(self, server_ip, server_port):
        self.socket = socket(AF_INET, SOCK_STREAM)
        self.socket.connect((server_ip, server_port))

    def close(self):
        self.socket.shutdown(SHUT_WR)
        self.socket.close()
        self.socket = None

    def send_image(self, image_data):

        # use struct to make sure we have a consistent endianness on the length
        length = pack('>Q', len(image_data))

        # sendall to make sure it blocks if there's back-pressure on the socket
        self.socket.sendall(length)
        self.socket.sendall(image_data)

        ack = self.socket.recv(1)

        # could handle a bad ack here, but we'll assume it's fine.

if __name__ == '__main__':
    cp = ClientProtocol()

    image_data = None
    with open('IMG_0077.jpg', 'r') as fp:
        image_data = fp.read()

    assert(len(image_data))
    cp.connect('127.0.0.1', 55555)
    cp.send_image(image_data)
    cp.close()

这篇关于在Python中通过套接字发送大量数据的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆