为啥在关闭客户端套接字时,他的进程会更改状态“Z”(僵尸)?

Posted

技术标签:

【中文标题】为啥在关闭客户端套接字时,他的进程会更改状态“Z”(僵尸)?【英文标题】:Why at close the client socket, his process changes the status 'Z' (Zombie)?为什么在关闭客户端套接字时,他的进程会更改状态“Z”(僵尸)? 【发布时间】:2022-01-10 10:24:31 【问题描述】:

说明

我正在用 python3 中的套接字做一个架构服务器-多客户端。

为此,我使用多处理库。 下面的代码,创建一个服务器监听客户端连接:

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("",PORT))
sock.listen(CLIENTS)
print(logFile().message(f"running ClassAdmin server, listen CLIENTS clients by port PORT...",True,"INFO"))
sockSSL = context.wrap_socket(sock,server_side=True)
while sockSSL:
    connection, address = sockSSL.accept()
    eventChildStop = multiprocessing.Event()
    subprocess = multiprocessing.Process(target=ClientListener, name="client", args=(connection, address))
    subprocess.start()

在上面的代码中,每个客户端都在一个子进程中执行。与multiprocessing.Process()

这会运行类ClientListener

class ClientListener:
    def __init__(self,conn,addr):
        try:
            self.conn, self.addr = conn, addr
            self.nick = ""
            self.__listenData()
        except (KeyboardInterrupt,SystemExit) as err:
            print(logFile().message(f"The host self.nick (self.addr[0]:self.addr[1]) left", True, "INFO"))
        except BaseException as err:
            type, object, traceback = sys.exc_info()
            file = traceback.tb_frame.f_code.co_filename
            line = traceback.tb_lineno
            print(logFile().message(f"err in file:line", True, "ERROR"))
        finally:
            try:
                ListClients().remove(self.conn)
                self.conn.close()
            except:
                None
            finally:
                Client(self.conn,self.addr).registre(self.nick,"DISCONNECTED",False)
    def __listenData(self):
        while True:
            data = self.conn.recv(1024)
            text = data.decode('utf-8')
            if text.startswith("sig."):
                exec(f"raise text.split('.')[1]")
            elif data:
                if text.startswith("HelloServer: "):
                    self.nick = text.replace("HelloServer: ","")
                    client = Client(self.conn,self.addr).registre(self.nick, "CONNECTED", False)
                    if client==False:
                        self.conn.send(b"sig.SystemExit(-5000,'The nick exists and is connected',True)")
                    else:
                        print(logFile().message(f"The host self.nick (self.addr[0]:self.addr[1]) is connected", True, "INFO"))
                        ListClients().add(self.conn)
                else:
                    print(data)

__init__()中运行__listenData()方法,该方法负责处理客户端在服务器端发送的数据。

__init__() 中,我处理异常以在关闭客户端时显示信息。

try:
    #...
finally:
    try:
        ListClients().remove(self.conn)
        self.conn.close()
    except:
        None
    finally:                             
        Client(self.conn,self.addr).registre(self.nick,"DISCONNECTED",False)
        #HERE, Can I close the current child process?

在这个try执行finally,因为总是会删除客户端列表中的客户端,如果有连接会关闭它。

问题

我的问题如下:

    我运行服务器....

    在客户端机器上,我运行客户端....

    当我在服务器上连接客户端时,在服务器进程中创建了一个子进程。

    现在客户端关闭了,所以在服务器中,如果我们显示子进程的状态变为Z,则意味着,僵尸

我的问题是……

如何关闭这个子进程?由于客户端在由multiprocessing.Process() 启动的子进程中运行。我必须使用multiprocessing 的方法terminate() 关闭它...我认为这就是解决方案。

可以解决吗?

我想...

    在根目录中添加侦听multiprocessing.Event() 的其他子进程:
while sockSSL:
    connection, address = sockSSL.accept()
    eventChildStop = multiprocessing.Event()
    subprocess = multiprocessing.Process(target=ClientListener, name="client", args=(connection, address,eventChildStop))
    subprocess.start()
    multiprocessing.Process(target=ClientListener.exitSubprocess, name="exitChildProcess",args=(eventChildStop, subprocess)).start()
    time.sleep(1)
    listenerClients 类中,我在__init__() 中添加参数event
class ClientListener:
    def __init__(self,conn,addr,event):
    我添加了静态方法exitSubprocess()。这个方法在理论上终止子进程(不是这样):
@staticmethod
    def exitSubprocess(event,process):
        while True:
            if event.is_set():
                print(process.id)
                process.terminate()
                break
            time.sleep(.5)

但是,事实并非如此,结果是一样的。子进程(一个是方法静态exitSubprocess。第一个是客户端进程)是状态Zombie。为什么...?

有人明白发生了什么吗?

我感谢有人回复。感谢您的关注。

【问题讨论】:

【参考方案1】:

解决方案

嗨!!问题解决了!!

我该如何解决?

我这样做是,在启动客户端的子进程之后,在父进程中启动一个线程,当子进程退出时,在退出之前,线程将子进程与父进程和线程成功退出。 最后,客户端的子进程退出。

要遵循的步骤

首先,在服务器的根代码中添加:

# This thread is responsible of close the client's child process
threading.Thread(target=ClientListener.exitSubprocess,name="closeChildProcess",args=(eventChildStop,subprocess,)).start()

结果完成:

while sockSSL:
    connection, address = sockSSL.accept()
    eventChildStop = multiprocessing.Event()
    subprocess = multiprocessing.Process(target=ClientListener, name="client", args=(connection, address,eventChildStop))

    # This thread is responsible of close the client's child process
    threading.Thread(target=ClientListener.exitSubprocess,name="closeChildProcess",args=(eventChildStop,subprocess,)).start()
    subprocess.start()
    time.sleep(1)

在新的exitSubprocess方法之后,我改变了:

if event.is_set():
    print(process.id)
    process.terminate()
    break

通过

if event.is_set():
    process.join()
    break

结果完成:

# This method get as argument the process child. For join it at parent process
@staticmethod
def exitSubprocess(event,process):
    while True:
        if event.is_set():
            process.join()
            break
        time.sleep(.5)

重要的是,在客户端的子进程中他最后一个finally 添加一个time.sleep(1) 1 秒。 给线程时间以将客户端的子进程加入父进程

class ClientListener:
def __init__(self,conn,addr,event):
    try:
        self.conn, self.addr = conn, addr
        self.nick = ""
        self.__listenData()
    except (KeyboardInterrupt,SystemExit) as err:
        print(logFile().message(f"The host self.nick (self.addr[0]:self.addr[1]) left", True, "INFO"))
    except BaseException as err:
        type, object, traceback = sys.exc_info()
        file = traceback.tb_frame.f_code.co_filename
        line = traceback.tb_lineno
        print(logFile().message(f"err in file:line", True, "ERROR"))
    finally:
        try:
            ListClients().remove(self.conn)
            self.conn.close()
        except:
            None
        finally:
            Client(self.conn,self.addr).registre(self.nick,"DISCONNECTED",False)
            event.set()
            # This will delay 1 second to close the proccess, for this gives time at exitSubprocess method to join the client's child process with the parent process
            time.sleep(1)

非常感谢您的关注和时间。

【讨论】:

以上是关于为啥在关闭客户端套接字时,他的进程会更改状态“Z”(僵尸)?的主要内容,如果未能解决你的问题,请参考以下文章

自己用C语言构造数据包,实现TCP三次握手过程,为啥中间会产生一个RST信号?

为啥套接字关闭 Erlang

如果在多个系统调用中完成,为啥 TCP 套接字会变慢?

c++ 服务器在客户端终止连接进程后不关闭 TCP 套接字连接

为啥我的客户端会杀死我的服务器?

为啥我的 TCP 服务器套接字在一个客户端断开连接后关闭?