【问题标题】:TCP client/server with sockets, server sending files to clients, client hangs, Python带套接字的 TCP 客户端/服务器、服务器向客户端发送文件、客户端挂起、Python
【发布时间】:2017-07-10 10:14:10
【问题描述】:

我想用 Python 中的套接字编写一个简单的 TCP 服务器。服务器应将图像发送到连接的客户端。客户端应该会收到图像。但是,现在,客户端只收到图像的一部分,我什至无法打开它。

服务器是使用 select 的多客户端,但这不是问题所在。我认为问题在于发送图像。

我希望这里的“协议”非常简单。

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

所以客户端只能向服务器发送“GET”消息,而服务器在得到“GET”字符串后,应该立即将整个图像发送给客户端。至此,通讯结束。

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

#!/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()

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

------------------------------------------ -------------------------------------------------- -------------------------------------- 编辑 ----------------------------------------------- -------------------------------------------------- ----------------------------------

根据 cmets 中一位用户的建议,我尝试实现一个简单的协议:

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

一切似乎都正常,但在图像传输之后,我的 CPU 100% 忙,正如top 所说。还有....

服务器的输出:

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

客户的输出:

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

表示客户端成功接收到图片。现在好吗?我的意思是,我从服务器获得了图像,但我不知道我是否正确实现了协议。也许这里可以改进一些东西?

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:

    #!/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()

或者我应该这样做:

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

代替:

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

在客户端?

【问题讨论】:

  • 您在 TCP 之上使用什么协议来发送和接收文件?如果您没有记录它,则需要 - 否则,几乎不可能判断客户端和服务器是否在做正确的事情。
  • @DavidSchwartz:我只想使用 TCP 套接字发送和接收图像 - 仅此而已,无需其他协议。
  • 那么你的客户端有一个严重的错误,它发送的字符串“GET”不是任何图像的一部分。 (老实说,我可以根据多年的经验告诉你——你确实需要准确地记录你的协议。请求是如何分隔的?服务器如何标记数据的结尾?客户端如何识别它?)跨度>
  • 在服务器代码中,使用sock.sendall(bytes) 而不是sock.send(bytes) - 这应该递归地发送整个图像文件
  • 很可能是额外的外部while True:。打开文件,接收数据,关闭文件,然后重新开始。文件再次打开并被截断,recv() 被调用但没有更多数据,文件被关闭。它应该是一个无限循环,或者只是挂起......这取决于服务器是否调用shutdown()来指示没有更多数据。

标签: python file sockets tcp


【解决方案1】:

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

服务器:

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

客户:

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, [])

服务器演示:

(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$ 

客户端演示:

(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$

如果您想了解更多关于如何完成选择循环驱动的网络,您可以细读the Twisted implementation

【讨论】:

  • 恐怕这不能回答我的问题,因为我只想使用套接字。
  • 好消息 - HTTP 是由套接字组成的。
  • 是的,这一切我都知道。我知道如何在 Python 中使用为此任务设计的库。我的问题只是关于插座。
  • 您的sockets-module 代码存在很多问题,因此任何没有“扔掉所有这些代码并在学习如何使用套接字后重写它”的答案都会产生误导。我不是要刻薄。我正在努力提供帮助。您的问题的前提有太多错误,无法建议一个简单的增量修复。
  • 当然,有很多方法可以发送数据。其中一些比其他更容易。不过,如果没有充分的理由,您可能不应该发明一种全新的方式来发送文件。这样做不是相当微不足道的,我在这个问题中没有看到任何迹象表明需要一种新的方法(例如,没有讨论 HTTP 是如何不合适的)。
【解决方案2】:

您的客户发送字符串“GET”。您只想发送和接收图像数据,“GET”不是图像数据。

您可能还有其他错误,如果不了解您的协议就很难判断。例如,一侧如何知道它得到了所有的图像数据?

【讨论】:

  • 我只是认为客户端发送 GET 就足够了,所以当服务器收到它时,它会简单地发送整个图像。就是这样,我想以这种方式实现它。但是,它似乎不能那样工作,因为我无法打开图像。
  • 服务器何时收到“它”?所以唯一合法的发送就是字符串“GET”?以及如何标记文件的结尾?请记录您的协议。如果您不指定正确的事情实际上是什么,就无法判断服务器是否在做正确的事情!
【解决方案3】:

编辑部分下面的代码对我来说似乎不错。 我完全同意@David Schwartz。如果你想实现一个协议,你必须考虑很多关于协议设计的事情。例如:

  • 如果您发送命令,服务器如何反应
  • 允许使用哪些命令
  • 为服务器响应等实现错误代码

为此,您可以阅读“TCP/IP 和 Linux 协议实现”,这是一本很好的书。

另一方面,你可以坚持基础并继续使用 TCP 套接字,我的谦虚建议是你实现一些机制让客户端知道它是否有完整的信息,例如:

  • 发送数据的哈希,在客户端检查哈希
  • 将信息切割成块并发送块数量,然后在客户端检查接收了多少块,并在失败时重新请求图像
  • 或以上所有

【讨论】:

    【解决方案4】:

    您可以阅读以下内容: https://picamera.readthedocs.io/en/release-1.12/recipes1.html 并学习很多关于如何通过套接字发送图片的知识。“基本食谱”为您提供所需的一切。

    【讨论】:

      【解决方案5】:

      这真的很奇怪。我尝试了两个不同的图像,并且代码有效。问题出在图像上。

      【讨论】:

      • 你完全没有抓住重点!你的代码只能靠运气运行,有时它会......直到它不起作用。
      • @DavidSchwartz:但是为什么呢?不明白,请解释一下
      • 就您的问题阅读我的 cmets。您从未设计过协议,因此无法知道该协议是否会可靠地工作。你会具体审查什么?如果没有协议,如何判断代码是否正确实现了协议?
      • @DavidSchwartz:我试过了,但图像发送部分仍然存在问题(请参阅我的编辑)。你能帮忙吗?
      • @DavidSchwartz:还有一个问题。如果我使用UDP代替TCP,我不需要设计协议吗?我理解对了吗?
      猜你喜欢
      • 1970-01-01
      • 2016-03-03
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      • 2013-04-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-04
      相关资源
      最近更新 更多