帶有套接字的TCP客戶端/服務器,向客戶端發送文件的服務器,客戶端掛起,Python

[英]TCP client/server with sockets, server sending files to clients, client hangs, Python


I want to write a simple TCP server using sockets in Python. The server should send the image to the connected client. The client should receive the image. But, for now on, the client receives only the portion of the image, and I can't even open it.

我想在Python中使用套接字編寫一個簡單的TCP服務器。服務器應將映像發送到連接的客戶端。客戶端應該收到圖像。但是,就目前而言,客戶端只接收圖像的一部分,我甚至無法打開它。

Server is multi-client using select, but its not the problem here. I think that the problem is with sending image.

服務器是使用select的多客戶端,但這不是問題所在。我認為問題在於發送圖像。

I wanted "the protocol" to be very simple here.

我希望“協議”在這里非常簡單。

SERVER                   CLIENT
               GET
       <----------------
              IMAGE
       ----------------->
      END OF COMMUNICATION

So the client can only send the "GET" message to server, and the server, after getting the "GET" string, should immediately send the whole image to client. That's it, communication is over.

因此客戶端只能向服務器發送“GET”消息,服務器在獲取“GET”字符串后,應立即將整個映像發送給客戶端。就是這樣,溝通就結束了。

server.py

server.py

#!/usr/bin/env python

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

image = 'image.png'

HOST = '127.0.0.1'
PORT = 6666

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)
                bytes = open(image).read()

                if data:
                    sock.send(bytes)

            except:
                sock.close()
                connected_clients_sockets.remove(sock)
                continue

server_socket.close()

client.py

client.py

#!/usr/bin/env python

import socket
import sys

HOST = '127.0.0.1'
PORT = 6666

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.connect(server_address)

try:

    sock.sendall("GET")

    while True:

        myfile = open('imagefromserv.png', 'w')

        while True:
            data = sock.recv(4096)
            if not data:
                break
            myfile.write(data)
        myfile.close()

finally:
    sock.close()

I'm using Python 2.7 on newest Ubuntu.

我在最新的Ubuntu上使用Python 2.7。

------------------------------------------------------------------------------------------------------------------------------------ EDIT ------------------------------------------------------------------------------------------------------------------------------------

-------------------------------------------------- -------------------------------------------------- --------------------------------編輯----------------- -------------------------------------------------- -------------------------------------------------- ---------------

Following advices given by one of the users in comments, I TRIED to implement a simple protocol:

根據評論中的一個用戶給出的建議,我試着實現一個簡單的協議:

CLIENT                                      SERVER
                      GET\r\n   
       ----------------------------------->
                      OK\r\n
       <----------------------------------- 
                   GET_SIZE\r\n
       ----------------------------------->
                    SIZE 1024\r\n
       <-----------------------------------
                   GET_IMG\r\n
       ----------------------------------->
                  IMG_DATA\r\r
       <-----------------------------------

Everything seems to work, but after the image transfer, my CPU is 100% busy, as top says. And ....

一切似乎都有效,但在圖像傳輸之后,我的CPU 100%忙碌,正如top所說。並....

Server's output:

服務器的輸出:

--GET--
--GET_SIZE--
--24518--
--GET_IMG--

Client's output:

客戶的輸出:

--OK--
--SIZE 24518--
--24518--
4096
8192
12288
16384
20480
24523
Image received successfully

Indicates that the client received the image successfully. Is it ok now? I mean, I got the image from server, but I do not know, if I implemented the protocol correctly. Maybe something can be improved here?

表示客戶端已成功接收圖像。現在好嗎?我的意思是,我從服務器獲得了圖像,但我不知道,如果我正確地實現了協議。也許這里可以改進一些東西?

client.py:

client.py:

#!/usr/bin/env python

import socket
import sys

HOST = '127.0.0.1'
PORT = 6666

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.connect(server_address)
fname = 'fromserver.png'

try:

    sock.sendall("GET\r\n")
    data = sock.recv(4096)

    if data:
        txt = data.strip()
        print '--%s--' % txt

        if txt == 'OK':

            sock.sendall("GET_SIZE\r\n")
            data = sock.recv(4096)

            if data:
                txt = data.strip()
                print '--%s--' % txt

                if txt.startswith('SIZE'):

                    tmp = txt.split()
                    size = int(tmp[1])

                    print '--%s--' % size

                    sock.sendall("GET_IMG\r\n")

                    myfile = open(fname, 'wb')

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

                        txt = data.strip('\r\n')

                        if 'EOF' in str(txt) :
                            print 'Image received successfully'
                            myfile.write(data)
                            myfile.close()
                        else :
                            myfile.write(data)
finally:
    sock.close()

server.py:

server.py:

    #!/usr/bin/env python

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

image = 'tux.png'

HOST = '127.0.0.1'
PORT = 6666

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)

                if data :

                    txt = data.strip()
                    print '--%s--'%txt

                    if txt == 'GET' :
                        sock.sendall('OK\r\n')

                    elif txt == 'GET_SIZE' :

                        with open ('tux.png','rb') as f1:
                            file_size = len(f1.read())
                            f1.seek(0)

                        print '--%s--'%file_size

                        file_size = '%s' % file_size
                        sock.sendall('SIZE %s\r\n' % file_size)

                    elif txt == 'GET_IMG' :
                        with open(image, 'rb') as fp:
                            image_data = fp.read()

                        msg = '%sEOF\r\r' % image_data
                        sock.sendall(msg)
                        print msg

            except:
                sock.close()
                connected_clients_sockets.remove(sock)
                continue

server_socket.close()

Or maybe I should rather do:

或許我應該做:

sock.sendall(image_data)
sock.sendall('EOF\r\n')

instead of:

代替:

msg = '%sEOF\r\n' % image_data
sock.sendall(msg)

in client?

在客戶?

5 个解决方案

#1


3  

The code below the EDIT section seems good to me. I completely agree with @David Schwartz. If you're looking to implement a protocol you have to take into consideration a whole lot of things regarding the protocol design. Ex:

編輯部分下面的代碼對我來說似乎很好。我完全贊同@David Schwartz。如果您正在尋求實現協議,則必須考慮有關協議設計的大量內容。例如:

  • if you send a command, how the server reacts
  • 如果發送命令,服務器如何反應
  • which commands are allowed
  • 允許哪些命令
  • implement error codes for server responses, etc
  • 實現服務器響應的錯誤代碼等

For that you can read "TCP/IP and Linux Protocol Implementation" it is a great book for such matter.

為此您可以閱讀“TCP / IP和Linux協議實現”,這是一本很好的書。

In the other hand, you can stick with the basics and continue using TCP sockets,my humble suggestion is that you implement some mechanism for the client to know if it has the complete information, like:

另一方面,你可以堅持使用基礎知識並繼續使用TCP套接字,我的拙見建議是你實現一些機制讓客戶知道它是否有完整的信息,如:

  • send a hash of the data, check the hash in the client
  • 發送數據的哈希值,檢查客戶端中的哈希值
  • cut the info in chunks and send the chunk quantity, then in the client check how many chunks were received, and re-request the image in case of failure
  • 以塊的形式剪切信息並發送塊數量,然后在客戶端檢查收到了多少塊,並在發生故障時重新請求圖像
  • or all of the above
  • 或以上所有

#2


11  

You accidentally forgot to just use HTTP and Twisted.

你不小心忘了使用HTTP和Twisted。

Server:

服務器:

from twisted.web.static import File
from twisted.web.resource import Resource
def resource():
    resource = Resource()
    resource.putChild(b"", File(u"xkcd/sandwich.png"))
    return resource

Client:

客戶:

from filepath import FilePath
from twisted.internet.task import react
from treq import get, content

def main(reactor):
    d = get(b"http://localhost:8080/")
    d.addCallback(content)
    d.addCallback(FilePath(u"image.png").setContent)
    return d

react(main, [])

Server demo:

服務器演示:

(everything) exarkun@baryon:/tmp/demo$ twist web --class server.resource
2017-02-23T21:32:14-0500 [-] Site starting on 8080
2017-02-23T21:32:14-0500 [twisted.web.server.Site#info] Starting factory <twisted.web.server.Site instance at 0x7fd1ef81a8c0>
2017-02-23T21:32:14-0500 [twisted.application.runner._runner.Runner#info] Starting reactor...
2017-02-23T21:33:01-0500 [twisted.python.log#info] "127.0.0.1" - - [24/Feb/2017:02:33:01 +0000] "GET / HTTP/1.1" 200 21489 "-" "-"
^C
2017-02-23T21:33:05-0500 [-] Received SIGINT, shutting down.
2017-02-23T21:33:05-0500 [-] (TCP Port 8080 Closed)
2017-02-23T21:33:05-0500 [twisted.web.server.Site#info] Stopping factory <twisted.web.server.Site instance at 0x7fd1ef81a8c0>
2017-02-23T21:33:05-0500 [-] Main loop terminated.
(everything) exarkun@baryon:/tmp/demo$ 

Client demo:

客戶端演示:

(everything) exarkun@baryon:/tmp/demo$ ls -l image.png
ls: cannot access 'image.png': No such file or directory
(everything) exarkun@baryon:/tmp/demo$ python client.py 
(everything) exarkun@baryon:/tmp/demo$ ls -l image.png 
-rwxr-xr-x 1 exarkun exarkun 21489 Feb 23 21:33 image.png
(everything) exarkun@baryon:/tmp/demo$

If you want to learn more about how select-loop driven networking is done, you can peruse the Twisted implementation.

如果您想了解有關如何完成選擇循環驅動網絡的更多信息,您可以仔細閱讀Twisted實現。

#3


5  

Your client sends the string "GET". You only want to send and receive image data and "GET" is not image data.

您的客戶端發送字符串“GET”。您只想發送和接收圖像數據,“GET”不是圖像數據。

You may have other bugs, it's hard to tell without understanding your protocol. For example, how does one side know what it has gotten all the image data?

您可能有其他錯誤,如果不了解您的協議,很難說清楚。例如,一方如何知道它獲得了所有圖像數據?

#4


3  

You could read this: https://picamera.readthedocs.io/en/release-1.12/recipes1.html and learn a lot on how to send pictures overs sockets.'Basic Recipes' gives you everything you need.

您可以閱讀:https://picamera.readthedocs.io/en/release-1.12/recipes1.html並了解如何在套接字上發送圖片。“基本食譜”為您提供所需的一切。

#5


-3  

This is really strange. I tried two different images, and the code worked. The problem was with the image then.

這真的很奇怪。我嘗試了兩個不同的圖像,代碼工作正常。問題在於圖像。


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2017/02/20/72f5fa80887c5bcf1aa70b96f630825.html



 
粤ICP备14056181号  © 2014-2021 ITdaan.com