python - 聊天室服务器在 SIGINT 时未关闭线程且未退出
问题描述
我在 Python 中有以下模拟聊天室的服务器程序。该代码接受来自客户端的连接,并为每个客户端启动一个新线程。该线程将等待来自该客户端的消息。消息可以L
使服务器响应连接的客户端列表,ip:port msg
服务器将消息发送msg
给客户端ip:port
。
在客户端将有 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()
该代码有一个信号处理程序,SIGINT
用于使退出更清晰(关闭连接,向客户端发送消息(仍有待实现)等等)。处理程序写入一个全局标志EXIT
以使无限循环终止。
- 代码在 Python2 和 Python3 中运行。
SIGINT
但是CTRL-C 生成的信号存在一些问题。当没有客户端连接时,使用 Python2 启动的程序会正确退出,而 Python3 中的程序则不会。为什么会有这种行为差异? - 考虑只在 Python2 中运行程序,当客户端连接并且我按下 CTRL-C 时,main while 退出,就像信号总是被主线程捕获,这会中断阻塞的系统调用
accept
。但是其他线程没有,我认为是因为阻塞了底层系统调用data = self.conn.recv(1024)
。在 CI 中会阻塞SIGINT
一个线程的信号,然后pthread_cancel
从另一个线程调用。SIGINT
在Python中生成时如何退出所有线程?
目前仅在 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()
解决方案
推荐阅读
- laravel - 如何以编程方式更改 Laravel Omnipay 对象?
- python - 将 url 参数传递给序列化程序
- python-3.x - GridSearchCV 和 RandomizedSearchCV (sklearn):TypeError:__call__() 缺少 1 个必需的位置参数:'y_true'
- python - reshape 函数如何 reshape 2 扁平化水平堆叠在一起的一维数组?
- c# - 包括和然后包括性能
- mongodb - 当我使用 find().cursor() 时,Mongoose 会消耗大量内存
- sql - 不使用聚合函数我们可以在sql中做pivot吗
- java - Spring中的连接错误,使用Mongodb休眠
- unicode - 诅咒 - 某些表情符号(标志)使终端输出变形
- maven - 使用 Maven 目标时,CXF 的 Thorntail 服务执行方式不同