Question

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

Was it helpful?

Solution

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)

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top