Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python:当客户端意外停止时,多线程套接字服务器将无休止地运行_Python_Multithreading_Sockets - Fatal编程技术网

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

我创建了一个多线程套接字服务器,使用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
。这会将线程设置为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