有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java CTRLC在Python中的行为不同

我最近开始学习Python(这里是长期Java程序员),目前正在编写一些简单的服务器程序。问题是,对于一段看似相似的代码,Java对应程序正确地响应SIGINT信号(Ctrl+C),而Python对应程序则没有。当使用单独的线程生成服务器时,可以看到这一点。代码如下:

// Java code

package pkg;

import java.io.*;
import java.net.*;

public class ServerTest {

    public static void main(final String[] args) throws Exception {
        final Thread t = new Server();
        t.start();
    }

}

class Server extends Thread {

    @Override
    public void run() {
        try {
            final ServerSocket sock = new ServerSocket(12345);
            while(true) {
                final Socket clientSock = sock.accept();
                clientSock.close();
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

}

和Python代码:

# Python code
import threading, sys, socket, signal

def startserver():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', 12345))
    s.listen(1)
    while True:
        csock, caddr = s.accept()
        csock.sendall('Get off my lawn kids...\n')
        csock.close()

if __name__ == '__main__':
    try:
        t = threading.Thread(target=startserver)
        t.start()
    except:
        sys.exit(1)

在上述两个代码片段中,我创建了一个简单的服务器,用于侦听给定端口上的TCP请求。在Java代码中按下Ctrl+C时,JVM退出,而在Python代码中,我得到的只是外壳上的一个^C。我可以停止服务器的唯一方法是按Ctrl+Z,然后手动终止进程

所以我想出了一个计划;为什么不让一个sighandler监听Ctrl+Z并退出应用程序?很酷,所以我想到:

import threading, sys, socket, signal

def startserver():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind(('', 12345))
    s.listen(1)
    while True:
        csock, caddr = s.accept()
        csock.sendall('Get off my lawn kids...\n')
        csock.close()

def ctrlz_handler(*args, **kwargs):
    sys.exit(1)

if __name__ == '__main__':
    try:
        signal.signal(signal.SIGTSTP, ctrlz_handler)
        t = threading.Thread(target=startserver)
        t.start()
    except:
        sys.exit(1)

但现在,我似乎让情况变得更糟了!现在在shell发射“^C”时按Ctrl+C,在shell发射“^Z”时按Ctrl+Z

所以我的问题是,为什么会有这种奇怪的行为?我可能会有多个服务器进程在同一个进程中作为单独的线程运行,所以当进程接收SIGINT时,有没有干净的方法杀死服务器?顺便说一句,在Ubuntu上使用Python 2.6.4

TIA,
佐助


共 (2) 个答案

  1. # 1 楼答案

    1. 您创建的线程不是守护线程,因此当父线程退出时它不会退出

    2. main在启动子线程后立即退出,python进程等待子线程终止

    3. offtop:不要忘记关闭插座

    4. 应该为子线程实现一些停止机制。一个简单的解决方法是:检查while循环中接受客户机连接的一些stopped标志(使用模块级别,或者扩展threading.Thread类来封装它),将该标志设置为True,并在Ctrl+C和/或Ctrl+Z或不使用它们的情况下(通过连接)启动服务器套接字;在主块中等待while t.isAlive(): time.sleep(1)。下面是一个示例代码:

    {pr 1}$

    我不知道为什么它会把stop、kick、run方法显示为模块级,并放置多个换行符,抱歉

  2. # 2 楼答案

    有几个问题:

    • 不要捕捉所有异常并悄悄退出。这是你能做的最糟糕的事情

    • 除非你真的知道自己在做什么,否则千万不要抓住SIGTSTP。这不是一个应用程序用来拦截的信号,如果你捕捉到了它,很可能是你做错了什么

    • 键盘中断只发送到程序的初始线程,而不会发送到其他线程。这样做有两个原因。通常,您希望在单个位置处理^C,而不是在碰巧接收到异常的随机、任意线程中。此外,它有助于保证在正常操作中,大多数线程不会收到异步异常;这很有用,因为(在所有语言中,包括Java)很难可靠地处理它们

    • 你永远不会在这里看到键盘中断,因为你的主线程在启动次线程后立即退出。没有接收异常的主线程,它只是被丢弃

    解决方法有两个:保持主线程,这样键盘中断就有了出路,只捕获键盘中断,而不是所有异常


    import threading, sys, socket, signal, time
    
    def startserver():
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(('', 12345))
        s.listen(1)
        while True:
            csock, caddr = s.accept()
            csock.sendall('Get off my lawn kids...\n')
            csock.close()
    
    if __name__ == '__main__':
        try:
            t = threading.Thread(target=startserver)
            t.start()
    
            # Wait forever, so we can receive KeyboardInterrupt.
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print "^C received"
            sys.exit(1)
    
        # We never get here.
        raise RuntimeError, "not reached"