Python:当客户端意外停止时,多线程套接字服务器将无休止地运行
我创建了一个多线程套接字服务器,使用python将许多客户端连接到服务器。若客户端由于异常而意外停止,服务器将不间断运行。有没有一种方法可以单独杀死服务器中的特定线程,并杀死其余正在运行的线程 服务器:Python:当客户端意外停止时,多线程套接字服务器将无休止地运行,python,multithreading,sockets,Python,Multithreading,Sockets,我创建了一个多线程套接字服务器,使用python将许多客户端连接到服务器。若客户端由于异常而意外停止,服务器将不间断运行。有没有一种方法可以单独杀死服务器中的特定线程,并杀死其余正在运行的线程 服务器: class ClientThread(Thread): def __init__(self,ip,port): Thread.__init__(self) self.ip = ip self.port = port print("New server
class ClientThread(Thread):
def __init__(self,ip,port):
Thread.__init__(self)
self.ip = ip
self.port = port
print("New server socket thread started for " + ip + ":" + str(port))
def run(self):
while True :
try:
message = conn.recv(2048)
dataInfo = message.decode('ascii')
print("recv:::::"+str(dataInfo)+"::")
except:
print("Unexpected error:", sys.exc_info()[0])
Thread._stop(self)
tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpServer.bind((TCP_IP, 0))
tcpServer.listen(10)
print("Port:"+ str(tcpServer.getsockname()[1]))
threads = []
while True:
print( "Waiting for connections from clients..." )
(conn, (ip,port)) = tcpServer.accept()
newthread = ClientThread(ip,port)
newthread.start()
threads.append(newthread)
for t in threads:
t.join()
客户:
def Main():
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,int(port)))
while True:
try:
message = input("Enter Command")
s.send(message.encode('ascii'))
except Exception as ex:
logging.exception("Unexpected error:")
break
s.close()
很抱歉,回答得太长了,但现在开始 你的代码有很多问题。首先,您的客户端实际上并没有关闭套接字,因为
s.close()
永远不会执行。循环在break
处中断,随后的任何内容都将被忽略。因此,为了好的编程,请更改这些语句的顺序,但这与您的问题无关
您的服务器代码在很多方面都是错误的。正如当前编写的那样,它永远不会退出。您的线程也不能正常工作。我已经修复了您的代码,使其成为一个可工作的多线程服务器,但它仍然不会退出,因为我不知道退出的触发器是什么。但让我们从主回路开始:
while True:
print( "Waiting for connections from clients..." )
(conn, (ip,port)) = tcpServer.accept()
newthread = ClientThread(conn, ip,port)
newthread.daemon = True
newthread.start()
threads.append(newthread) # Do we need this?
for t in threads:
t.join()
我已经将conn
的传递添加到您的客户机线程中,其原因很快就会显现出来。但是,您的while True
循环永远不会中断,因此您将永远不会进入for循环,在该循环中加入线程。如果您的服务器打算无限期运行,那么这根本不是问题。只要拆下for循环,这部分就可以了。您不需要仅仅为了连接线程而连接线程。连接线程只允许程序阻塞,直到线程完成执行
另一个添加是newthread.daemon=True
。这会将线程设置为daemonic,这意味着它们将在主线程退出后立即退出。现在,即使存在活动连接,服务器也会响应control+c
如果您的服务器注定是永无止境的,那么也不需要在主循环中将线程存储到threads
列表中。当每次客户端连接和断开连接时都会添加一个新条目时,此列表会不断增加,这会泄漏内存,因为您没有使用线程
列表进行任何操作。我保持了它的原样,但仍然没有退出无限循环的机制
然后让我们继续讨论你的主题。如果希望简化代码,可以使用函数替换运行部分。在这种情况下,不需要对Thread进行子类化,但这是可行的,因此我保留了您的结构:
class ClientThread(Thread):
def __init__(self,conn, ip,port):
Thread.__init__(self)
self.ip = ip
self.port = port
self.conn = conn
print("New server socket thread started for " + ip + ":" + str(port))
def run(self):
while True :
try:
message = self.conn.recv(2048)
if not message:
print("closed")
try:
self.conn.close()
except:
pass
return
try:
dataInfo = message.decode('ascii')
print("recv:::::"+str(dataInfo)+"::")
except UnicodeDecodeError:
print("non-ascii data")
continue
except socket.error:
print("Unexpected error:", sys.exc_info()[0])
try:
self.conn.close()
except:
pass
return
首先,我们将conn
存储到self.conn
。您的版本使用了conn
变量的全局版本。当您与服务器有多个连接时,这会导致意外结果conn
实际上是在accept为客户端连接创建的一个新套接字,它对每个线程都是唯一的。这就是服务器区分客户端连接的方式。它们侦听一个已知端口,但当服务器接受连接时,accept会为该特定连接创建另一个端口并返回该端口。这就是为什么我们需要将其传递给线程,然后从self.conn
而不是全局conn
读取
服务器“挂起”客户端连接错误,因为在循环中没有检测此错误的机制。如果客户端关闭连接,socket.recv()
不会引发异常,但不会返回任何内容。这是您需要检测的情况。我敢肯定,您甚至不需要在此处尝试/例外,但这并不有害-但您需要在此处添加您期望的例外。在这种情况下,捕获除之外的所有未声明的都是错误的。您还有另一个语句可能引发异常。如果您的客户端发送了无法用ascii编解码器解码的内容,您将得到UnicodeDecodeError
(在此处尝试此操作而不进行错误处理,将telnet发送到您的服务器端口,并将一些希伯来文或日文复制粘贴到连接中,看看会发生什么情况)。如果您只是捕获了所有内容并将其视为套接字错误,那么您现在将输入代码的线程结束部分,因为您无法解析消息。通常我们只是忽略“非法”信息,然后继续。我加了这个。如果要在收到“坏”消息时关闭连接,只需将self.conn.close()
和return
添加到此异常处理程序
然后,当您确实遇到套接字错误时,或者客户端关闭了连接,您将需要关闭套接字并退出线程。您将在套接字上调用close()
,将其封装在try/except中,因为您并不关心它是否因不再存在而失败
当您想退出线程时,只需从run()
循环中返回。执行此操作时,线程将有序退出。就这么简单
然后还有另一个潜在的问题,如果您不仅要打印消息,还要对它们进行解析,并对接收到的数据进行处理。这个我不修,但留给你
TCP套接字传输数据,而不是消息。在构建通信协议时,不能假设当您的recv
返回时,它将返回一条消息。当您的recv()
返回某些内容时,它可能表示以下五种情况之一:
客户端已关闭连接,未返回任何内容
只有一条完整的消息,您会收到它
只有部分消息。要么是因为您在客户端传输所有数据之前读取了套接字,要么是因为客户端发送了超过2048字节的数据(即使您的客户端从未发送超过2048字节的数据,恶意客户端也肯定会尝试此操作)
有多条消息正在等待,您已全部收到
如4所示,但最后一条消息是部分的
大多数套接字编程错误都是r