使用AF_UNIX套接字时的python asyncore问题

3 投票
2 回答
2934 浏览
提问于 2025-04-16 17:35

我在使用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 个回答

1

你的问题主要是因为你在使用SOCK_DGRAM这种类型的套接字。从我了解的情况来看,使用asyncore来处理SOCK_DGRAM套接字是行不通的,因为它没有提供recvfromsendto这两个功能。此外,socat似乎也无法处理SOCK_DGRAM的UNIX域套接字。

SOCK_DGRAM套接字没有真正的连接概念,所以在使用select函数时,它们总是会被标记为可写。但是当你实际执行write操作时,会失败,因为你没有提供目标地址。

另一个回答中的术语用得不太准确,但大体上是对的。你在这里需要使用SOCK_STREAM套接字。

5

注意 正如其他回答所指出的,当你发送数据包时,需要指定接收方。目前你的 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_connecthandle_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)

撰写回答