我最近开始学习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
at贝壳。我可以停止服务器的唯一方法是按 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处按 Ctrl + C 发出'^ C'并在shell处按下 Ctrl + Z '^ Z'。
所以我的问题是,为什么这种奇怪的行为?我可能有多个服务器进程在同一进程中作为单独的线程运行,所以在进程收到SIGINT
时杀死服务器的任何干净方法? BTW在Ubuntu上使用Python 2.6.4。
TIA,
佐助
答案 0 :(得分:5)
几个问题:
不要捕获所有异常并以静默方式退出。这是你可以做的最糟糕的事情。
除非你真的知道自己在做什么,否则永远不要抓住SIGTSTP。这不是一个意图让应用程序拦截的信号,如果你抓住它,你可能会做错事。
KeyboardInterrupt只发送到程序的初始线程,而不会发送到其他线程。这样做有几个原因。通常,您希望在单个位置处理^ C,而不是在接收异常的随机任意线程中处理。此外,它有助于保证在正常操作中,大多数线程永远不会收到异步异常;这很有用,因为(在所有语言中,包括Java),它们很难可靠地处理。
你永远不会在这里看到KeyboardInterrupt,因为你的主线程在启动辅助线程后立即退出。没有主线程来接收异常,它只是被丢弃了。
修复是双重的:保持主线程,所以KeyboardInterrupt有一个地方可去,只捕捉KeyboardInterrupt,而不是所有例外。
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"
答案 1 :(得分:1)
您的create不是守护进程的线程,所以当父线程退出时它不会退出;
主要出口启动子线程后,python进程等待子线程终止;
offtop:不要忘记关闭套接字;
你应该为子线程实现一些停止机制。一个简单的workround:检查接受客户端连接的while循环中的一些stopped
标志(使用模块级,或扩展threading.Thread
类来封装它),将该标志设置为True并踢出服务器socket +(通过连接)在Ctrl + C和/或Ctrl + Z上或没有它们;等待while t.isAlive(): time.sleep(1)
的主要区块。以下是示例代码:
import threading, sys, socket, signal, time
class Server(threading.Thread): def init(self): threading.Thread.init(self) self._stop = False self._port =12345
def stop(self): self.__stop = True self.kick()
def kick(self): try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('', self.__port)) s.close() except IOError, e: print "Failed to kick the server:", e
def run(self): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', self._port)) s.listen(1) try: while True: csock, caddr = s.accept() if self._stop: print "Stopped, breaking" break print "client connected from", caddr try: csock.sendall('Get off my lawn kids...\n') finally: csock.close() finally: s.close()
if name == 'main': t = Server() # if it is failed to stop the thread properly, it will exit t.setDaemon(True) t.start() try: while t.isAlive(): time.sleep(1) except: # Ctrl+C brings us here print "Maybe, you have interrupted this thing?" t.stop() # t.join() may be called here, or another synchronization should appear to # make sure the server stopped correctly
我不知道为什么它显示停止,踢,运行方法作为模块级别并放置多个换行符,抱歉