Problems with python asyncore working with AF_UNIX sockets
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() ?
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)
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.
精彩评论