python asyncore 使用 AF_UNIX 套接字时出现问题

发布于 2024-11-06 21:43:14 字数 1360 浏览 0 评论 0原文

我在使用 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 套接字?

I have some problems using asyncore with AF_UNIX sockets. This code

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()

If i execute the code

 $ python select_prova.py
 writable
 handle_connect
 handle_write
 handle_close
 $  

It quits immediatly, and doesn't wait for read and write. If i change code to force writable() method to return always False, it wait correctly for input and i can communicate with socat like this

 $ socat readline UNIX:/tmp/mysocket

But only for reading (write logically doesn't works because writable() returns False). Are there error in my code or I can't manage AF_UNIX sockets with asyncore/select() ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

宁愿没拥抱 2024-11-13 21:43:14

注意 正如其他答案所指出的,当您发送数据报时,您需要指定接收者。就目前情况而言,您的 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)

Note As the other answer points out, when you send a datagram you need to specify the receiver. As it stands, your testselect class looks more like a client than a server.

Review some of these asyncore examples to find a server pattern you can copy. The TimeChannel example is closer to what you want -- change socket.AF_INET to socket.AF_UNIX and use a socket path for the bind address to have it use a UNIX domain socket.


You're setting socket.SOCK_DGRAM which usually indicates creation of a UDP INET socket. Unix domain sockets are a form of IPC. You should change it to socket.SOCK_STREAM, call self.listen([backlog]), implement handle_accept(), etc.

If you did intend to use SOCK_DGRAM with AF_UNIX, the reason your server exits is that it is indicating writable as soon as it's started, which causes handle_write to run, sending the packet containing 'buffer' immediately.

If you want your server to wait until it's received a packet before replying, set the buffer in handle_connect or handle_read:

    def __init__(self):
        ...
        self.buffer = ''

    def handle_connect(self):
        self.buffer = 'buffer'

Now when you start your server it'll wait until it receives a packet from socat.


I've rewritten your example to work more like you indend:

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)
记忆里有你的影子 2024-11-13 21:43:14

您的困难可以归结为您正在使用 SOCK_DGRAM。据我所知,您基本上无法使用 asyncore 有效处理 SOCK_DGRAM 套接字(没有 recvfromsendto)。此外,socat 似乎没有办法使用 SOCK_DGRAM UNIX 域套接字。

SOCK_DGRAM 套接字没有真正的连接概念,因此它们始终在 select 调用中注册为可写。但是当您实际执行write时,它将失败,因为您没有提供目标地址。

另一个答案有术语错误,但基本上是正确的。这里需要使用 SOCK_STREAM 套接字。

Your difficulty can be boiled down to the fact you're using SOCK_DGRAM. From what I can tell, you basically cannot effectively handle SOCK_DGRAM sockets with asyncore (no recvfrom or sendto). Additionally, socat does not seem to have a way to work with SOCK_DGRAM UNIX domain sockets.

SOCK_DGRAM sockets have no real notion of a connection, so they will always register as writeable in a select call. But when you actually do the write it will fail because you're not supplying a destination address.

The other answer has terminology wrong, but is basically correct. You need to use a SOCK_STREAM socket here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文