让我们在python中考虑这段代码:
import socket
import threading
import sys
import select
class UDPServer:
def __init__(self):
self.s=None
self.t=None
def start(self,port=8888):
if not self.s:
self.s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.s.bind(("",port))
self.t=threading.Thread(target=self.run)
self.t.start()
def stop(self):
if self.s:
self.s.close()
self.t.join()
self.t=None
def run(self):
while True:
try:
#receive data
data,addr=self.s.recvfrom(1024)
self.onPacket(addr,data)
except:
break
self.s=None
def onPacket(self,addr,data):
print addr,data
us=UDPServer()
while True:
sys.stdout.write("UDP server> ")
cmd=sys.stdin.readline()
if cmd=="start\n":
print "starting server..."
us.start(8888)
print "done"
elif cmd=="stop\n":
print "stopping server..."
us.stop()
print "done"
elif cmd=="quit\n":
print "Quitting ..."
us.stop()
break;
print "bye bye"
它运行一个交互式shell,我可以使用它来启动和停止UDP服务器。 服务器是通过一个类来实现的,该类启动一个线程,其中try / except块内有一个无限循环的 recv / onPacket 回调,它应该检测错误和退出从循环。 我期望的是当我在shell上键入“stop”时,套接字被关闭,并且 recvfrom 函数引发异常,因为文件描述符无效。 相反,即使在关闭调用之后,似乎 recvfrom 仍然阻止等待数据的线程。 为何这种奇怪的行为? 我总是使用这种模式来实现C ++和JAVA中的UDP服务器,它始终有效。
我还尝试使用“选择”将带有套接字的列表传递给 xread 参数,以便从<<>获取文件描述符中断事件strong>选择而不是 recvfrom ,但选择似乎对关闭“不敏感”。
我需要一个独特的代码,在Linux和Windows上使用python 2.5 - 2.6维护相同的行为。
感谢。
答案 0 :(得分:3)
通常的解决方案是让管道告诉工人线程何时死亡。
使用os.pipe
创建管道。这为您提供了一个在同一程序中具有读写结束的套接字。它返回原始文件描述符,您可以按原样使用os.read
和os.write
),或使用os.fdopen
转换为Python文件对象。
工作线程使用select.select
等待 网络套接字和管道的读取端。当管道变得可读时,工作线程会清理并退出。不要读取数据,忽略它:它的到来就是信息。
当主线程要杀死工作线程时,它会将一个字节(任何值)写入管道的写入端。然后主线程加入工作线程,然后关闭管道(记得关闭两端)。
P.S。在多线程程序中关闭正在使用的套接字是一个坏主意。 Linux close(2)联机帮助页说:
关闭文件描述符可能是不明智的,因为它们可能在同一进程中的其他线程中被系统调用使用。由于文件描述符可能会被重复使用,因此有一些模糊的竞争条件可能会导致意外的副作用。
所以很幸运,你的第一种方法不起作用!
答案 1 :(得分:1)
这不是java。好提示:
以下是使用twisted的示例:
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor, stdio
from twisted.protocols.basic import LineReceiver
class UDPLogger(DatagramProtocol):
def datagramReceived(self, data, (host, port)):
print "received %r from %s:%d" % (data, host, port)
class ConsoleCommands(LineReceiver):
delimiter = '\n'
prompt_string = 'myserver> '
def connectionMade(self):
self.sendLine('My Server Admin Console!')
self.transport.write(self.prompt_string)
def lineReceived(self, line):
line = line.strip()
if line:
if line == 'quit':
reactor.stop()
elif line == 'start':
reactor.listenUDP(8888, UDPLogger())
self.sendLine('listening on udp 8888')
else:
self.sendLine('Unknown command: %r' % (line,))
self.transport.write(self.prompt_string)
stdio.StandardIO(ConsoleCommands())
reactor.run()
示例会话:
My Server Admin Console!
myserver> foo
Unknown command: 'foo'
myserver> start
listening on udp 8888
myserver> quit