tonysimpson / nanomsg-python

nanomsg wrapper for python with multiple backends (CPython and ctypes) should support 2/3 and Pypy
MIT License
381 stars 85 forks source link

Unable to bind inside a Process #30

Closed fbor closed 9 years ago

fbor commented 9 years ago

I tried to use nanomsg-python inside a Process (using multiprocessing.Process) with PIPELINE protocol (many clients PUSH to one server who PULLed). I do not know why, the socket which must received messages does not work (and no logs or errors are returned from server side with NN_PRINT_ERRORS or NN_PRINT_STATISTICS). But if I code the same thing using a thread, it works (replacing multiprocessing.Process with threading.Thread in the following code). Is there any incidence with how processes are managed with python ?

For instance you can use the following code for a server which PULLed nanomsg socket to reproduce the issue.

import nanomsg, multiprocessing

class ServerSocket(multiprocessing.Process):

def __init__(self, server):
    super(ServerSocket, self).__init__()
    self.daemon = True
    self.server=server

def on_receive(self, msg):
    print msg

def run(self):
    self.socket = nanomsg.Socket(nanomsg.PULL)
    self.socket.bind(self.server)
    while True:
        msg=self.socket.recv()
        self.on_receive(msg)
    self.socket.close()

And after you can ue it with the following code

myserver=ServerSocket('tcp:\\*:12345')
myserver.start()

No socket seems to be created by nanomsg (no traces with NN_PRINT_STATISTICS set).

pikeas commented 9 years ago

Any resolution to this?

tonysimpson commented 9 years ago

Great news! I didn't find any issue with nanomsg or nanomsg-python

Here is some test code based on your original which should work.

import multiprocessing
import nanomsg

class ServerSocket(multiprocessing.Process):
    def __init__(self, address):
        super(ServerSocket, self).__init__()
        self.daemon = False # False is the default, just here to highlight the issue with original code
        self._address = address

    def on_receive(self, msg):
        print msg

    def run(self):
        with nanomsg.Socket(nanomsg.PULL) as socket:
            socket.bind(self._address)
            msg = socket.recv()
            self.on_receive(msg)

if __name__ == "__main__":
    address = 'tcp://0.0.0.0:12345'
    my_server = ServerSocket(address)
    my_server.start()

    with nanomsg.Socket(nanomsg.PUSH) as client_socket:
        client_socket.connect(address)
        client_socket.send('nanomsg')

    # Because ServerSocket only expects one message and the main thread will 
    # wait for it to finish (it is not a daemon) the program should end
    # after the message is printed.

Why does this code work and the original code not?

The main reason is setting daemon to true on the Process, daemonic Processes will exit as soon as there parent does, thus in my tests the run method is never even called when daemon == True - I think this is the main cause of your issue.

With that resolved the other issue in the original code is very apparent "\" ( \ character in string) should be "//" in the connection address, also I would use "0.0.0.0" for all interfaces rather than "*" - I think this is the more common approach.

A rule I always try to remember is blame the simple things before the complex things, this can save you a lot of time when working with new technology, my justification is the simple things are better understood and easier to discount and therefore a better place to start identifying issues.