使用AF_UNIX套接字时的python asyncore问题
我在使用asyncore和AF_UNIX套接字时遇到了一些问题。这个代码
import asyncore, socket, os
class testselect(asyncore.dispatcher):
path = '/tmp/mysocket'
def __init__(self):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_UNIX, socket.SOCK_DGRAM)
self.bind(self.path)
self.buffer = 'buffer'
def handle_connect(self):
print 'handle_connect'
pass
def handle_close(self):
print 'handle_close'
if os.path.exists(self.path)
os.remove(self.path)
self.close()
def handle_read(self):
print 'handle_read'
print self.recv(8192)
def writable(self):
print 'writable'
return (len(self.buffer) > 0)
def handle_write(self):
print 'handle_write'
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
client = testselect()
asyncore.loop()
如果我执行这个代码
$ python select_prova.py
writable
handle_connect
handle_write
handle_close
$
它会立刻退出,并且不等待读取和写入。如果我把代码改成让writable()方法总是返回False
,那么它就会正确地等待输入,我可以像这样与socat进行通信
$ socat readline UNIX:/tmp/mysocket
但这只适用于读取(写入实际上不工作,因为writable()返回False
)。我的代码中有错误吗,还是我无法用asyncore/select()来管理AF_UNIX套接字?
2 个回答
你的问题主要是因为你在使用SOCK_DGRAM这种类型的套接字。从我了解的情况来看,使用asyncore来处理SOCK_DGRAM套接字是行不通的,因为它没有提供recvfrom
或sendto
这两个功能。此外,socat似乎也无法处理SOCK_DGRAM的UNIX域套接字。
SOCK_DGRAM套接字没有真正的连接概念,所以在使用select函数时,它们总是会被标记为可写。但是当你实际执行write
操作时,会失败,因为你没有提供目标地址。
另一个回答中的术语用得不太准确,但大体上是对的。你在这里需要使用SOCK_STREAM套接字。
注意 正如其他回答所指出的,当你发送数据包时,需要指定接收方。目前你的 testselect
类看起来更像是一个客户端,而不是服务器。
可以参考一些 asyncore 示例
,找一个你可以复制的服务器模式。TimeChannel
的例子更接近你想要的——把 socket.AF_INET
改成 socket.AF_UNIX
,并使用一个套接字路径作为绑定地址,这样就能使用 UNIX 域套接字了。
你设置了 socket.SOCK_DGRAM
,这通常表示创建一个 UDP INET 套接字。Unix 域套接字是一种进程间通信(IPC)方式。你应该把它改成 socket.SOCK_STREAM
,调用 self.listen([backlog])
,实现 handle_accept()
等等。
如果你确实打算用 SOCK_DGRAM 和 AF_UNIX,服务器退出的原因是它在启动后就显示为 writable
,这会导致 handle_write
运行,立即发送包含 'buffer'
的数据包。
如果你希望服务器在收到数据包后再回复,可以在 handle_connect
或 handle_read
中设置缓冲区:
def __init__(self):
...
self.buffer = ''
def handle_connect(self):
self.buffer = 'buffer'
现在,当你启动服务器时,它会等到收到来自 socat
的数据包。
我已经重写了你的例子,使其更符合你的意图:
import asyncore, socket, os
class testselect(asyncore.dispatcher):
path = '/tmp/mysocket'
def __init__(self):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(self.path)
self.listen(5)
def handle_accept(self):
client = self.accept()
if client is None:
pass
else:
handler = testhandler(*client)
class testhandler(asyncore.dispatcher_with_send):
def __init__(self, sock, addr):
asyncore.dispatcher_with_send.__init__(self, sock)
self.addr = addr
self.buffer = 'greetings'
def handle_read(self):
print self.recv(8192)
def writable(self):
return (len(self.buffer) > 0)
def handle_write(self):
self.send(self.buffer)
self.buffer = ''
def handle_close(self):
self.close()
server = testselect()
try:
asyncore.loop()
finally:
if os.path.exists(testselect.path):
os.unlink(testselect.path)