多线程 TCP 套接字



我正在尝试创建一个可以一次处理多个套接字请求的线程TCP套接字服务器。

为了测试它,我在客户端启动了几个线程,看看我的服务器是否可以处理它。第一个插座打印成功,但我得到了其他插座的[Errno 32] Broken pipe。 我不知道如何避免它。

import threading
import socketserver
import graphitesend

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024)
if data != "":
print(data)
class ThreadedTCPServer(socketserver.ThreadingTCPServer):
allow_reuse_address = True
def __init__(self, host, port):
socketserver.ThreadingTCPServer.__init__(self, (host, port), ThreadedTCPRequestHandler)
def stop(self):
self.server_close()
self.shutdown()
def start(self):
threading.Thread(target=self._on_started).start()
def _on_started(self):
self.serve_forever()
def client(g):
g.send("test", 1)
if __name__ == "__main__":
HOST, PORT = "localhost", 2003
server = ThreadedTCPServer(HOST, PORT)
server.start()
g = graphitesend.init(graphite_server = HOST, graphite_port = PORT)
threading.Thread(target = client, args=(g,)).start()
threading.Thread(target = client, args=(g,)).start()
threading.Thread(target = client, args=(g,)).start()
threading.Thread(target = client, args=(g,)).start()
threading.Thread(target = client, args=(g,)).start()
threading.Thread(target = client, args=(g,)).start()
threading.Thread(target = client, args=(g,)).start()
server.stop()

确定您到底期望发生什么有点困难,但我认为近似原因是您在杀死服务器之前没有给客户端运行时间。

构造Thread对象并调用其start方法时,将创建一个线程,并使其准备好运行。然后,它将被放置在系统上的"可运行"任务队列中,但它将与主线程和所有其他线程(实际上在同一台机器上的所有其他任务(竞争CPU时间。

您的多个线程(main 和其他线程(也可能被 python 解释器的 GIL(全局解释器锁 - 假设您使用的是"标准"CPython(序列化,这意味着它们甚至可能还没有"走出大门"。

但是,在他们有机会发送任何内容之前,您将server_close()关闭服务器。这与"管道断开"错误一致:其余客户端正在尝试写入已被"远程"端关闭的套接字。

您应该在创建线程对象时收集它们并将它们放在列表中(以便以后可以引用它们(。完成创建并启动所有这些线程后,返回列表并在每个线程对象上调用.join方法。这将确保线程有机会完成。只有这样,您才应该关闭服务器。像这样:

threads = []
for n in range(7):
th = threading.Thread(target=client, args=(g,))
th.start()
threads.append(th)
# All threads created. Wait for them to finish.
for th in threads:
th.join()
server.stop()

需要注意的另一件事是,所有客户端都共享要发送到服务器的同一单个连接,因此您的服务器永远不会创建多个线程:就其而言,只有一个客户端。如果您实际上希望每个客户端都有单独的连接,则可能应该将graphitesend.init移动到客户端函数中。

(免责声明:我对graphitesend一无所知,除了我可以在谷歌上对第一个结果进行 15 秒的浏览;我假设它基本上只是TCP连接的包装器。

最新更新