gpt4 book ai didi

python - Python:客户端意外停止时,多线程套接字服务器将无限运行

转载 作者:行者123 更新时间:2023-12-01 09:32:25 25 4
gpt4 key购买 nike

我创建了一个多线程套接字服务器,以使用python将许多客户端连接到该服务器。如果客户端由于异常而意外停止,则服务器将不间断运行。有没有一种方法可以杀死服务器中的那个特定线程,而其余线程仍在运行

服务器:

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。这会将您的线程设置为守护程序,这意味着它们将在您的主线程退出后立即退出。现在,即使有 Activity 的连接,您的服务器也可以对control + c做出响应。

如果您的服务器永无止境,那么也不需要在主循环中将线程存储到 threads列表。这个列表一直在增长,因为每次客户端连接和断开连接时都会添加一个新条目,这会泄漏内存,因为您没有将 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()不会引发异常,但不返回任何内容。这是您需要检测的条件。我相当确定您甚至不需要在这里尝试/异常(exception),但是这不会造成伤害- 但您需要在中添加您期望在此处添加的异常(exception)。在这种情况下,使用未声明的 except捕获所有内容都是错误的。您还有另一条语句可能会引发异常。如果您的客户端发送了无法用ascii编解码器解码的内容,则会得到 UnicodeDecodeError(尝试在此处没有错误处理,将telnet连接到您的服务器端口,并将希伯来语或日语复制粘贴到连接中,看看会发生什么)。如果您只是捕获了所有内容并将其视为套接字错误,那么现在您将因为无法解析消息而输入代码的线程结尾部分。通常,我们只是忽略“非法”消息并继续进行。我添加了这个。如果要在收到“不良”消息时关闭连接,只需将 self.conn.close()return也添加到此异常处理程序中。

然后,当您确实遇到套接字错误-或客户端已关闭连接时,您将需要关闭套接字并退出线程。您将在套接字上调用 close()-将其封装在try/except中,因为您并不在乎它是否会因为不再存在而失败。

当您想退出线程时,只需从 return循环中添加 run()即可。执行此操作时,您的线程将有序退出。就如此容易。

然后,如果您不仅要打印消息,还要分析它们并对接收到的数据进行某些处理,则还有另一个潜在的问题。我没有解决这个问题,但留给您。

TCP套接字传输数据,而不是消息。建立通信协议(protocol)时,不得假定 recv返回时,它将返回一条消息。当您的 recv()返回某些内容时,可能意味着以下五种情况之一:
  • 客户端已关闭连接,但未返回任何内容
  • 只有一则完整的消息,您会收到
  • 只有部分消息。要么是因为您在客户端传输所有数据之前就已经读取了套接字,要么是因为客户端发送了超过2048字节的数据(即使您的客户端从未发送超过2048字节的数据,恶意客户端也肯定会尝试这样做)
  • 有不止一条消息正在等待,您都收到了所有
  • 为4,但最后一条消息是部分消息。

  • 大多数套接字编程错误都与此有关。程序员希望2发生(就像您现在所做的那样),但它们不能满足3-5。相反,您应该分析收到的内容并采取相应的措施。如果数据似乎少于完整的消息,请将其存储在某处,然后等待更多数据出现。当出现更多数据时,将它们串联起来,看看现在是否有完整的消息。并且,当您从该缓冲区中解析了一条完整的消息时,请检查缓冲区中是否有更多数据-下一条消息的第一部分,或者如果客户端速度快且服务器速度慢,甚至还有更多的完整消息。如果您处理一条消息然后擦除缓冲区,则可能还擦除了下一条消息中的字节。

    关于python - Python:客户端意外停止时,多线程套接字服务器将无限运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49850472/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com