我正在尝试创建一个可以一次处理多个套接字请求的线程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()
答案 0 :(得分:0)
确定您期望发生的事情有点困难,但我认为最近的原因是您在杀死服务器之前没有给客户提供时间。
构造Thread
对象并调用其start
方法时,您正在创建一个线程,并准备好运行它。然后它将被放置在系统上的“可运行”任务队列中,但它将与您的主线程和所有其他线程(以及同一台机器上的所有其他任务)竞争CPU时间。
您的多个线程(主要加上其他线程)也可能被python解释器的GIL(Global Interpreter Lock - 序列化 - 假设您正在使用“标准”CPython)这意味着他们甚至可能没有“出局”大门“还没有。
但是,在他们有机会发送任何内容之前,您将使用server_close()
关闭服务器。这与“Broken Pipe”错误一致:您的其余客户端正在尝试写入已被“远程”端关闭的套接字。
您应该在创建线程对象时收集它们并将它们放在一个列表中(以便以后可以引用它们)。当您完成创建并启动所有这些时,然后返回列表并在每个线程对象上调用.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连接的包装。)