简体   繁体   English

聊天室服务器未关闭线程且在SIGINT时未退出

[英]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来使无限循环终止。

  1. The code runs both in Python2 and Python3. 该代码可在Python2和Python3中运行。 However there are some problems with 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? 为什么会有这种行为上的差异?
  2. Considering only running the program in Python2, when a client connects and I press CTRL-C, the main while exits, like the signal is catched always by the main thread and this interrupts the blocking system call 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM