6

I am learning to use HTML5 WebSockets and as part of that I am writing a server in Python so I can know the nitty gritty of how they work. I created one the other day that worked pretty well, but I wanted to expand it so it would support multiple endpoints with each endpoint being a different "service" which can handle websocket clients.

At the moment, my implementation works with spawning processes and such (I am using multiprocessing instead of threading since I read that threading isn't really multithreading in CPython and that's what I think I am using (default install on Ubuntu 12.04)), but I am having trouble sending received client sockets to the service processes.

Here is how I send them (this runs in a loop):

try:
    #get a new client
    conn, addr = server.accept()
    print "Client connected from", addr
    request = conn.recv(4096)
    response, close, service = self.handshake(request)
    conn.send(response)
    if close:
        print "Invalid request from", addr
        conn.close()
        continue
    client = WebSockets.WebSocketClient(conn, addr)
    service.clientConnQueue.put(client)

'server' is a socket listening for incoming connections. The handshake method takes care of validating their request and determining which service process to put the client into. 'response' is the http response to send, 'close' is True if there was an error, and 'service' is a class which inherits from multiprocessing.Queue. WebSocktes.WebSocketClient is where my sending and receiving implementation is stored and it basically functions as a wrapper for a socket.

'clientConnQueue' is created like so:

class Service(multiprocessing.Process):
    """Base class for all services."""
    def __init__(self, manager):
        multiprocessing.Process.__init__(self)
        self.manager = manager
        self.clientConnQueue = self.manager.Queue()
        self.shutdownFlag = multiprocessing.Event()

manager is a multiprocessing.Manager()

The error I get when I try to place a client in the clientConnQueue is as follows:

File "./WebSocketServer.py", line 183, in <module>
main()
File "./WebSocketServer.py", line 180, in main
server.runServer()
File "./WebSocketServer.py", line 67, in runServer
service.clientConnQueue.put(client)
File "<string>", line 2, in put
File "/usr/lib/python2.7/multiprocessing/managers.py", line 758, in _callmethod
conn.send((self._id, methodname, args, kwds))
TypeError: expected string or Unicode object, NoneType found

I then get a broken pipe error on the receiving side.

I got the same error when I was using a multiprocessing.Queue to send the connection and I thought that changing it to a queue created by a manager would fix the problem. However, it seems to do the exact same implementation.

Clearly this isn't the way one is supposed to send something like this to a running process, so what is the proper way to be sending non-serializable objects to a process?

Los Frijoles
  • 4,771
  • 5
  • 30
  • 49

4 Answers4

1

Pass socket to another process is not a trivial thing. Look, for example, this question: Can I open a socket and pass it to another process in Linux

Anyway, OS processes or threads is not what you really want for implement websocket server, because of large memory overhead. Look at smth with nonblocking sockets... eg, Tornado http://www.tornadoweb.org/documentation/websocket.html

Community
  • 1
  • 1
seriyPS
  • 6,817
  • 2
  • 25
  • 16
  • 1
    Each client socket won't necessarily get its own thread, but each service does get its own process/thread and it is left up to the service exactly what to do with the socket. That is a good point though...http servers don't have to deal with the possibility of 1000 simultaneous long lived connections and that is a lot of memory overhead if each connection had its own thread. – Los Frijoles May 28 '12 at 18:39
1

It's been around for 4+ years, though takes a little bit of work.

The guts are hanging around in multiprocessing.reduction, and the details of an example can be seen in this github gist.

Josiah
  • 727
  • 5
  • 15
0

You can't send sockets to forked processes without using deep unix magic. You can read the book Advanced Programming in the Unix environment if you really want to see how it is done in C.

For an easy solution change to using threading and your code will probably just work. While threading is not perfect in Python it is pretty good for doing IO intensive stuff.

Alternatively create your listening socket before forking and get all the subprocesses to listen (call accept). The os will ensure that only one of them gets the connection. This is the way preforking servers are usually written.

Nick Craig-Wood
  • 52,955
  • 12
  • 126
  • 132
0

If you create socket before process forking, then its file descriptor will be inherited by the children.

VSE NN
  • 1
  • 1