Java CTRL-C在Python中的行为不同
我最近开始学习Python(这里是长期Java程序员),目前正在编写一些简单的服务器程序。问题是,对于一段看似相似的代码,Java对应程序正确地响应Java CTRL-C在Python中的行为不同,java,python,sockets,Java,Python,Sockets,我最近开始学习Python(这里是长期Java程序员),目前正在编写一些简单的服务器程序。问题是,对于一段看似相似的代码,Java对应程序正确地响应SIGINT信号(Ctrl+C),而Python对应程序则没有。当使用单独的线程生成服务器时,就会看到这种情况。代码如下: //Java代码 包装; 导入java.io.*; 导入java.net。*; 公共类服务器测试{ 公共静态void main(最终字符串[]args)引发异常{ 最终线程t=新服务器(); t、 start(); } } 类服
SIGINT
信号(Ctrl+C),而Python对应程序则没有。当使用单独的线程生成服务器时,就会看到这种情况。代码如下:
//Java代码
包装;
导入java.io.*;
导入java.net。*;
公共类服务器测试{
公共静态void main(最终字符串[]args)引发异常{
最终线程t=新服务器();
t、 start();
}
}
类服务器扩展线程{
@凌驾
公开募捐{
试一试{
最终服务器插座=新服务器插座(12345);
while(true){
最终套接字clientSock=sock.accept();
clientSock.close();
}
}捕获(例外e){
e、 printStackTrace();
}
}
}
和Python代码:
#Python代码
导入线程、系统、套接字、信号
def startserver():
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s、 绑定(“”,12345))
s、 听(1)
尽管如此:
csock,caddr=s.accept()
csock.sendall('离开我的草坪孩子…\n')
csock.close()
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
尝试:
t=threading.Thread(目标=startserver)
t、 开始()
除:
系统出口(1)
在上述两个代码片段中,我创建了一个简单的服务器,用于侦听给定端口上的TCP请求。在Java代码中按下Ctrl+C时,JVM退出,而在Python代码中,我得到的只是shell中的^C
。停止服务器的唯一方法是按Ctrl+Z,然后手动终止进程
所以我想出了一个计划;为什么不让一个sighandler监听Ctrl+Z并退出应用程序?酷,所以我想出了:
导入线程、系统、套接字、信号
def startserver():
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s、 绑定(“”,12345))
s、 听(1)
尽管如此:
csock,caddr=s.accept()
csock.sendall('离开我的草坪孩子…\n')
csock.close()
def ctrlz_处理器(*args,**kwargs):
系统出口(1)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
尝试:
signal.signal(signal.SIGTSTP,ctrlz_处理器)
t=threading.Thread(目标=startserver)
t、 开始()
除:
系统出口(1)
但现在,我似乎把情况弄得更糟了!现在,在外壳上按Ctrl+C键发射“^C”,在外壳上按Ctrl+Z键发射“^Z”
所以我的问题是,为什么会有这种奇怪的行为?我可能会有多个服务器进程在同一进程中作为单独的线程运行,因此当进程接收到SIGINT
时,是否有任何干净的方法杀死服务器?顺便说一句,在Ubuntu上使用Python2.6.4
TIA,佐助几个问题:
- 不要捕获所有异常并静默退出。这是你能做的最糟糕的事情
- 除非你真的知道自己在做什么,否则千万不要抓住SIGTSTP。这并不是一个让应用程序拦截的信号,如果你捕捉到了它,很可能是你做错了什么
- 键盘中断只发送到程序的初始线程,而不会发送到其他线程。这样做有两个原因。通常,您希望在单个位置处理^C,而不是在碰巧接收异常的随机、任意线程中。此外,它有助于保证在正常操作中,大多数线程不会收到异步异常;这很有用,因为(在所有语言中,包括Java)很难可靠地处理它们
- 在这里您永远不会看到键盘中断,因为主线程在启动次线程后立即退出。没有主线程来接收异常,它只是被丢弃
stopped
标志(使用模块级别,或者扩展threading.Thread
类来封装它),将该标志设置为True,并(通过连接)在Ctrl+C和/或Ctrl+Z上启动服务器套接字,或者不使用它们;在主块中等待,而t.isAlive():time.sleep(1)
。下面是一个示例代码: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"
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