2

Let say we have a server application written in Python.

Let also say that this main server process forked two more processes at the startup.

Server awaits its clients, and when one comes decides to which of two forked processes should pass the client's socket.

I do not want to fork a process each time a client comes; I want to have fixed number of servers, but one main server that receives a connection, then pass it to a server that deals with a specific work client asked for.

This should be a DOS attack protection, job separation, etc. etc.

Is there any trick to pass a Python object between started Python programs.

Some shared memory or something like that?

Would pickling the socket object and pushing it through IPC work?

martineau
  • 119,623
  • 25
  • 170
  • 301
Dalen
  • 4,128
  • 1
  • 17
  • 35
  • I don't think you can (or should) serialize things like sockets. Why not use an sync/event architecture? –  Jun 23 '16 at 12:59
  • That is the thing. It shouldn't be a problem to serialise it etc. but a socket object is half C half Python, so anything could happen in the process and this wouldn't be stable for sure. – Dalen Jun 23 '16 at 13:02
  • @Dalen, I have _no idea_ if this is the right thing to do, but I was able to share arbitrary objects (even remotely) via the following snippets: [stackoverflow.com](http://stackoverflow.com/questions/35370410/ssl-conceptual-aspects-of-sharing-a-object-via-pythons-dataprocess-object-remote) **I have no clue on security/stability/performance, though.** In your case (with sockets) I wouldn't try to make them into remote-shared-objects, but you can abstract from the link and apply this elsewhere, perhaps. – krysopath Jun 23 '16 at 13:19
  • @krysopath : Thanks, but I did such things, and even crazier ones before. Remotely sharing a socket wouldn't be possible. If you want to forward a connection to another PC, there are ways but firewalls object to real switchings and it is complicated to the point of uselessness. I.e. it's easy to tell the client to redirect to another IP instead. – Dalen Jun 23 '16 at 13:46
  • @Dalen Yes, indeed: Remotely sharing a socket is defeating the sockets purpose :) I just thought this method of sharing objects between your processes could help you with job-separation and the ilk. I was able to abuse the snippet to implement an "informationbroker" running on localhost, which gets asked by another one of localhosts processes, but answers only to valid requests. It may not achieve your purpose. – krysopath Jun 23 '16 at 13:56
  • Off-topic: Since you're doing socket programming, you might find this talk on [Python Concurrency](https://www.youtube.com/watch?v=MCs5OvhV9S4) from PyCon 2015 very interesting. – martineau Jun 23 '16 at 14:09

1 Answers1

6

Would pickling the socket object and pushing it through IPC work?

No. Inside that object is a file descriptor or handle to the kernel socket. It's just a number that the process uses to identify the socket when making system calls.

If you pickle that Python socket object and send it to another process, that process will be using a handle for a socket it didn't open. Or worse, that handle may refer to a different open file.

The most efficient way to handle this (on Linux) is like this:

  • Master process opens listening socket (e.g. TCP port 80)
  • Master process forks N children who all inherit that open socket
  • They all call accept() and block, waiting for a new connection
  • When a new client connects, the kernel will select one of the processes with a handle to that socket to accept the connection; the others will continue to wait

This way, you let the kernel handle the load balancing.

If you don't want this behavior, there is a way (in UNIX) to pass an open socket to another process. Again, this is more than just the handle; the kernel effectively copies the open socket to your processs's open file list. This mechanism is known as SCM_RIGHTS, and you can see an example (in C) here: http://man7.org/tlpi/code/online/dist/sockets/scm_rights_send.c.html

Otherwise, your master process will need to effectively proxy the connection to the child processes, reducing thr efficiency of the system.

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • Yes, yes, I know, but if you can somehow force a socket into a shared memory, then it is practically the same socket or not. I think that it should be possible to share handles between processes, at least on Unix, as this is what happens when you fork a process with an opened PIPE. Any other idea? – Dalen Jun 23 '16 at 13:06
  • What you can do is to parse the request and do the processing via a separate node. You may try using some queuing mechanism (kombu etc.) and sent the serialized objects via this pipeline. This would keep your two components separate enough that in case one of them fails the second can keep running and process these requests, and communication via queues is pretty straight forward . Enjoy! – daTokenizer Jun 23 '16 at 13:10
  • @daTokenizer : definitely something I do not want to do. Each process must parse the headers and decide what to do with them. The main process must not even answer to the client. Just decide where to forward the connection. – Dalen Jun 23 '16 at 13:16
  • The first solution, to let kernel decide which process gains the connection is very nice trick. Does it work for sure? The trouble is, I am already forcing kernel to check and recheck opened sockets using select(). So this would perhaps be an overkill - to check each socket in each process for read() write() readyness over and over again. – Dalen Jun 23 '16 at 13:32
  • My server is a mixture between asyncore and threaded client handling. I.e. some things are processed as in asyncore.py, some are pushed through threads, so clients are complicated, but it gains efficiency, abusing kernel as is and pushing too long jobs into threads, while processing quick ones within something like asyncore.loop() at once. Also, it is unkillable by a client, whatever it does and whatever bug is in a responding method. So your second way should do the trick. But I am concerned that all this may result in too much system calls and become heavy. – Dalen Jun 23 '16 at 13:38
  • It of course depends on specifics, but I think you'll end up with fewer net system calls. – Jonathon Reinhart Jun 23 '16 at 17:27