【问题标题】:In this Python 3 client-server example, client can't send more than one message在这个 Python 3 客户端-服务器示例中,客户端不能发送多个消息
【发布时间】:2014-11-02 01:22:10
【问题描述】:

这是一个简单的客户端-服务器示例,服务器返回客户端发送的任何内容,但反过来。

服务器:

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024)
        print('RECEIVED: ' + str(self.data))
        self.request.sendall(str(self.data)[::-1].encode('utf-8'))

server = socketserver.TCPServer(('localhost', 9999), MyTCPHandler)
server.serve_forever()

客户:

import socket
import threading

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('localhost',9999))

def readData():
    while True:
        data = s.recv(1024)
        if data:
            print('Received: ' + data.decode('utf-8'))

t1 = threading.Thread(target=readData)
t1.start()

def sendData():
    while True:
        intxt = input()
        s.send(intxt.encode('utf-8'))

t2 = threading.Thread(target=sendData)
t2.start()

我从我在 Google 上找到的一个示例中获取了服务器,但客户端是从头开始编写的。这个想法是让客户端可以无限期地从服务器发送和接收数据。

使用客户端发送第一条消息是可行的。但是当我尝试发送第二条消息时,我收到了这个错误:

ConnectionAbortedError: [WinError 10053] 已建立连接 被主机中的软件中止

我做错了什么?

【问题讨论】:

  • @vaultah:我不确定这真的是一个骗局。根本问题是相同的,但其中一个是使用低级别的socket 调用,另一个是TCPServer,所以你知道它是一个重复的唯一方法是如果你已经知道TCPServer 是如何工作的。

标签: python sockets python-3.x client-server


【解决方案1】:

对于TCPServer,处理程序的handle 方法被调用一次以处理整个会话。这在文档中可能并不完全清楚,但socketserver 与 stdlib 中的许多库一样,旨在作为清晰的示例代码以及直接使用,这就是文档链接到 the source 的原因,其中您可以清楚地看到,每个连接只会调用一次handleTCPServer.get_request 定义为仅在套接字上调用accept)。

因此,您的服务器收到一个缓冲区,发回响应,然后退出,关闭连接。

要解决这个问题,您需要使用循环:

def handle(self):
    while True:
        self.data = self.request.recv(1024)
        if not self.data:
            print('DISCONNECTED')
            break
        print('RECEIVED: ' + str(self.data))
        self.request.sendall(str(self.data)[::-1].encode('utf-8'))

一些旁注:

首先,单独使用BaseRequestHandler 只允许您一次处理一个客户端连接。正如introduction in the docs 所说:

这四个类同步处理请求;必须先完成每个请求,然后才能开始下一个请求。如果每个请求都需要很长时间才能完成,这是不合适的,因为它需要大量的计算,或者因为它返回的数据很多,客户端处理速度很慢。解决方案是创建一个单独的进程或线程来处理每个请求; ForkingMixInThreadingMixIn 混合类可用于支持异步行为。

这些 mixin 类在介绍的其余部分以及farther down the pageat the bottom 中进行了进一步描述,最后还有一个很好的示例。文档没有说清楚,但是如果您需要在处理程序中执行任何 CPU 密集型工作,您需要 ForkingMixIn;如果您需要在处理程序之间共享数据,则需要ThreadingMixIn;否则,您选择哪个并不重要。

请注意,如果您尝试同时处理大量客户端(超过几十个),那么分叉和线程都不是真正合适的——这意味着TCPServer 并不真正合适。对于这种情况,您可能需要asyncio 或第三方库(Twisted、gevent 等)。


致电str(self.data) 是个坏主意。您将获得与源代码兼容的字节字符串表示,例如b'spam\n'。您想要的是将字节字符串解码为等效的 Unicode 字符串:self.data.decode('utf8')


无法保证一侧的每个 sendall 都会与另一侧的单个 recv 匹配。 TCP 是字节流,而不是消息流;完全有可能在一个recv 中收到半条消息,在下一个中收到两条半消息。当系统在轻负载下在 localhost 上使用单个连接进行测试时,它可能看起来“工作”,但是一旦您尝试部署任何假设每个 recv 都收到一条消息的代码,您的代码就会中断.有关详细信息,请参阅Sockets are byte streams, not message streams。请注意,如果您的消息只是文本行(如您的示例中所示),则使用 StreamRequestHandler 及其 rfile 属性而不是 BaseRequestHandler 及其 request 属性可以轻松解决此问题。


您可能想要设置server.allow_reuse_address = True。否则,如果您退出服务器并重新启动它太快,它会失败并出现类似OSError: [Errno 48] Address already in use 的错误。

【讨论】:

  • 此服务器设置是否能够接受多个客户端连接?如果我理解正确,将为每个新客户端创建一个处理程序类的新实例。此解决方案适用于单个客户端,但当我尝试将第二个客户端连接到服务器时它不起作用。
  • @DavidMcDavidson:它可以连续接受多个客户端连接,但同时不能超过一个。这在链接文档页面的介绍中有详细说明,在同一页面的最底部有一个用ThreadingMixin 修复它的好例子。
  • 感谢旁注
猜你喜欢
  • 2014-10-17
  • 2021-12-04
  • 2019-02-26
  • 2015-01-05
  • 2017-01-12
  • 2023-02-10
  • 2013-04-01
  • 1970-01-01
  • 2021-12-12
相关资源
最近更新 更多