【问题标题】:chat-room server not closing threads and not exiting when SIGINTSIGINT 时聊天室服务器未关闭线程且未退出
【发布时间】:2019-01-05 14:23:42
【问题描述】:

我有以下模拟聊天室的 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 以使无限循环终止。

  1. 代码在 Python2 和 Python3 中运行。但是,CTRL-C 生成的SIGINT 信号存在一些问题。当没有客户端连接时,使用 Python2 启动的程序会正确退出,而 Python3 中的程序则不会。为什么会有这种行为差异?
  2. 考虑仅在 Python2 中运行程序,当客户端连接并且我按下 CTRL-C 时,main while 退出,就像信号总是被主线程捕获,这会中断阻塞系统调用 accept。但是其他线程没有,我认为是因为阻塞底层系统调用data = self.conn.recv(1024)。在 C 语言中,我会阻止一个线程的SIGINT 信号,然后从另一个线程调用pthread_cancel。 Python生成SIGINT时如何退出所有线程?

目前仅在 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()

【问题讨论】:

  • 你对EXIT = False的赋值是创建一个局部变量;你需要在sig_handler 中声明它global EXIT;至少在 python2 中......这可能会解决您的一些问题。
  • 另外,您的客户端线程没有通知服务器正在关闭的机制。您正在加入,但似乎没有终止条件(除了可能是远程断开连接,这可能是您的意图)。在任何情况下都最好设置超时 - 否则只要远程连接打开,您的进程就会一直存在。
  • 是的,还有待实施,但我先解决这个问题。
  • 虽然是全球性的。我以前纠正过,但后来我失去了纠正。谢谢

标签: python python-3.x server signals python-2.x


【解决方案1】:

关于 Python 3 中 accept() 的行为差异,请查看 docs 中的完整描述。我认为这是关键声明:

在 3.5 版中更改:如果系统调用被中断并且信号处理程序未引发异常,则该方法现在重试系统调用而不是引发 InterruptedError 异常(有关基本原理,请参阅 PEP 475)。

倒数第二句话中提到的另一个问题:

Python 2 生成 SIGINT 时如何退出所有线程?

查看threading 文档:

可以将线程标记为“守护线程”。这个标志的意义在于,当只剩下守护线程时,整个 Python 程序就退出了。初始值继承自创建线程。该标志可以通过 daemon 属性设置。

【讨论】:

    猜你喜欢
    • 2018-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    • 2012-08-11
    相关资源
    最近更新 更多