Python:如何在等待recv数据时关闭UDP套接字?
让我们考虑一下 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 函数会引发异常。 相反,即使在 close 调用之后, recvfrom 似乎仍然会阻塞等待数据的线程。 为什么会有这种奇怪的行为? 我一直使用这种模式在 C++ 和 JAVA 中实现 UDP 服务器,并且它总是有效。
我还尝试使用“select”将带有套接字的列表传递给 xread 参数,以便从 select 获取文件描述符中断事件 而不是来自 recvfrom,但 select 似乎对 close 也“不敏感”。
我需要一个独特的代码,可以在 Linux 和 Windows 上使用 python 2.5 - 2.6 保持相同的行为。
谢谢。
let's consider this code in 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"
It runs an interactive shell with which I can start and stop an UDP server.
The server is implemented through a class which launches a thread in which there's a infinite loop of recv/onPacket callback inside a try/except block which should detect the error and the exits from the loop.
What I expect is that when I type "stop" on the shell the socket is closed and an exception is raised by the recvfrom function because of the invalidation of the file descriptor.
Instead, it seems that recvfrom still to block the thread waiting for data even after the close call.
Why this strange behavior ?
I've always used this patter to implements an UDP server in C++ and JAVA and it always worked.
I've tried also with a "select" passing a list with the socket to the xread argument, in order to get an event of file descriptor disruption from select instead that from recvfrom, but select seems to be "insensible" to the close too.
I need to have a unique code which maintain the same behavior on Linux and Windows with python 2.5 - 2.6.
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
通常的解决方案是让管道告诉工作线程何时终止。
使用
os.pipe
创建管道。这为您提供了一个在同一程序中具有读取和写入端的套接字。它返回原始文件描述符,您可以按原样使用(os.read
和os.write
)或使用os.fdopen< 将其转换为 Python 文件对象/code>.
工作线程使用
select.select
等待网络套接字和管道的读取端。当管道变得可读时,工作线程将进行清理并退出。不要读取数据,忽略它:它的到达就是消息。当主线程想要杀死工作线程时,它会向管道的写入端写入一个字节(任何值)。然后主线程加入工作线程,然后关闭管道(记得关闭两端)。
PS 在多线程程序中关闭正在使用的套接字是一个坏主意。 Linux close(2) 联机帮助页显示:
所以幸运的是你的第一种方法没有奏效!
The usual solution is to have a pipe tell the worker thread when to die.
Create a pipe using
os.pipe
. This gives you a socket with both the reading and writing ends in the same program. It returns raw file descriptors, which you can use as-is (os.read
andos.write
) or turn into Python file objects usingos.fdopen
.The worker thread waits on both the network socket and the read end of the pipe using
select.select
. When the pipe becomes readable, the worker thread cleans up and exits. Don't read the data, ignore it: its arrival is the message.When the master thread wants to kill the worker, it writes a byte (any value) to the write end of the pipe. The master thread then joins the worker thread, then closes the pipe (remember to close both ends).
P.S. Closing an in-use socket is a bad idea in a multi-threaded program. The Linux close(2) manpage says:
So it's lucky your first approach did not work!
这不是java。好的提示:
这是一个使用twisted的示例:
示例会话:
This is not java. Good hints:
Here's an example using twisted:
Example session: