[英]chat-room server not closing threads and not exiting when SIGINT
I have the following server program in Python which simulates a chat-room. 我在Python中有以下服务器程序,它可以模拟聊天室。 The code accepts connections from clients and for each of them it launches a new thread. 该代码接受来自客户端的连接,并为每个客户端启动一个新线程。 This thread will wait for messages from this client. 该线程将等待来自此客户端的消息。 The messages can be L
so that the server will respond with a list of connected clients, ip:port msg
the server will send the message msg
to the client ip:port
. 该消息可以是L
,这样服务器将与连接的客户端列表响应, ip:port msg
服务器将发送消息msg
到客户端ip:port
。
On client side there will be 2 threads, one for receiving messages from the server, the other for sending. 在客户端,将有2个线程,一个用于接收来自服务器的消息,另一个用于发送。
import socket
from threading import Thread
#from SocketServer import ThreadingMixIn
import signal
import sys
import errno
EXIT = False
address = []
address2 = []
# handler per il comando Ctrl+C
def sig_handler(signum, frame):
if (signum == 2):
print("Called SIGINT")
EXIT = True
signal.signal(signal.SIGINT, sig_handler) # setto l'handler per i segnali
# Multithreaded Python server : TCP Server Socket Thread Pool
class ClientThread(Thread):
def __init__(self,conn,ip,port):
Thread.__init__(self)
self.conn = conn
self.ip = ip
self.port = port
print ("[+] New server socket thread started for " + ip + ":" + str(port))
def run(self):
while True:
data = self.conn.recv(1024)
print ("Server received data:", data)
if (data=='L'):
#print "QUI",address2
tosend = ""
for i in address2:
tosend = tosend + "ip:"+str(i[0]) + "port:"+str(i[1])+"\n"
self.conn.send(tosend)
#mandare elenco client connessi
else:
#manda ip:port msg
st = data.split(" ")
msg = st[1:]
msg = ' '.join(msg)
print ("MSG 2 SEND: ",msg)
ipport = st[0].split(":")
ip = ipport[0]
port = ipport[1]
flag = False
print ("Address2:",address2)
print ("ip:",ip)
print ("port:",port)
for i in address2:
print (i[0],ip,type(i[0]),type(ip),i[1],type(i[1]),port,type(port))
if str(i[0])==str(ip) and str(i[1])==str(port):
i[2].send(msg)
self.conn.send("msg inviato")
flag = True
break
if flag == False:
self.conn.send("client non esistente")
if __name__ == '__main__':
# Multithreaded Python server : TCP Server Socket Program Stub
TCP_IP = '127.0.0.1'
TCP_PORT = 2004
TCP_PORTB = 2005
BUFFER_SIZE = 1024 # Usually 1024, but we need quick response
tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpServer.bind((TCP_IP, TCP_PORT))
tcpServerB = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpServerB.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcpServerB.bind((TCP_IP, TCP_PORTB))
threads = []
tcpServer.listen(4)
tcpServerB.listen(4)
while True:
print("Multithreaded Python server : Waiting for connections from TCP clients...")
try:
(conn, (ip,port)) = tcpServer.accept()
except socket.error as e: #(code, msg):
if e.errno != errno.EINTR:
raise
else:
break
address.append((ip,port,conn))
(conn2, (ip2,port2)) = tcpServerB.accept()
address2.append((ip2,port2,conn2))
newthread = ClientThread(conn,ip,port)
newthread.start()
threads.append(newthread)
if EXIT==True:
break
print ("SERVER EXIT")
for t in threads:
t.join()
The code has a signal handler for SIGINT
to make the exit cleaner (closing connections, sending a message to the client (still to be implemented) and so on ). 该代码具有一个用于SIGINT
的信号处理程序,以使出口清洁器(关闭连接,向客户端发送消息(尚待实现)等)。 The handler writes a global flag EXIT
to make the infinite loops terminate. 处理程序编写一个全局标志EXIT
来使无限循环终止。
SIGINT
signal generated by CTRL-C. 但是,CTRL-C生成的SIGINT
信号存在一些问题。 When there is no client connected the program launched with Python2 exits correctly while the one in Python3 does not. 当没有客户端连接时,使用Python2启动的程序可以正确退出,而使用Python3的程序则无法退出。 Why this behavioural difference? 为什么会有这种行为上的差异? accept
. 考虑仅在Python2中运行该程序,当客户端连接并按CTRL-C时,主线程退出,就像信号始终被主线程捕获一样,这会中断阻塞的系统调用accept
。 However the other threads do not, I think because of the blocking underlying system call data = self.conn.recv(1024)
. 但是其他线程没有,我认为是因为底层系统调用data = self.conn.recv(1024)
被阻塞。 In CI would block SIGINT
signals for one thread and then call pthread_cancel
from the other thread. 在CI中,将阻止一个线程的SIGINT
信号,然后从另一个线程调用pthread_cancel
。 How to exit from all threads when SIGINT
is generated in Python? 在Python中生成SIGINT
时如何从所有线程退出? The client program that for the moment works in Python2 only and suffers from the same problem is: 目前仅在Python2中工作并且遭受相同问题的客户端程序是:
# Python TCP Client A
import socket
from threading import Thread
class ClientThread(Thread):
def __init__(self,conn):
Thread.__init__(self)
self.conn = conn
def run(self):
while True:
data = self.conn.recv(1024)
print "Ricevuto msg:",data
host = socket.gethostname()
print "host:",host
port = 2004
portB = 2005
BUFFER_SIZE = 2000
tcpClientA = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpClientB = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcpClientA.connect(('127.0.0.1', port))
tcpClientB.connect(('127.0.0.1', portB))
newthread = ClientThread(tcpClientB)
newthread.start()
while(True):
msg = raw_input("Inserisci comando: ")
tcpClientA.send (msg)
data = tcpClientA.recv(BUFFER_SIZE)
print "data received:",data
tcpClientA.close()
As for the difference in behavior with accept()
in Python 3, look at the full description in the docs . 至于Python 3中accept()
在行为上的差异,请参阅docs中的完整描述。 I think this is the key statement: 我认为这是关键的声明:
Changed in version 3.5: If the system call is interrupted and the signal handler does not raise an exception, the method now retries the system call instead of raising an InterruptedError exception (see PEP 475 for the rationale). 在版本3.5中进行了更改:如果系统调用被中断并且信号处理程序没有引发异常,则该方法现在重试系统调用,而不是引发InterruptedError异常(有关原理,请参阅PEP 475)。
The other problem, stated in your penultimate sentence: 在倒数第二句中指出的另一个问题是:
How to exit from all threads when SIGINT is generated in Python 2? 在Python 2中生成SIGINT时如何从所有线程退出?
Take a look at the threading documentation: 看一下线程文档:
A thread can be flagged as a “daemon thread”. 线程可以标记为“守护程序线程”。 The significance of this flag is that the entire Python program exits when only daemon threads are left. 该标志的重要性在于,仅保留守护程序线程时,整个Python程序都会退出。 The initial value is inherited from the creating thread. 初始值是从创建线程继承的。 The flag can be set through the daemon property. 可以通过daemon属性设置该标志。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.