Multi threaded Socket Server in PHP with fork
When you create your first socket server you will quickly realise that one thread is not enough. In a single thread architecture a server can’t handle more then one connection at the time. It’s undesired because in most cases there will be simultaneous connections.
There are few different ways to create a multi threaded socket server in PHP. The one I’m going to show is with use of pcntl_fork(). The idea is to fork the application on every new connection. Fork creates a new child process to handle the client while parent process can go back to listening for a new connection. Just to be clear, fork creates a new process not a thread therefore title of this post should be “multi processed socket server” (as it was pointed out to me by @Zipleen).
When fork creates a child process it duplicates parent’s memory for the child. Each process has own memory scope so changing variable in one won’t affect the others (it’s almost true). When fork duplicates a PHP resource (socket connection is a resource) it only copies a reference to the same place. If two (or more) processes try to use the same resource at the same time unexpected results might occur. It’s not recommended.
In the example I’m going to show it shouldn’t be a problem. Each process will operate on one resource only.
There is one more thing to remember. Use fork only in a CLI application. Forks are not designed to work on a web server.
I’ve created an example which can be downloaded from GitHub.
1 2 3 4 |
$ git clone https://github.com/lukaszkujawa/php-multithreaded-socket-server.git socketserver $ cd socketserver $ php server.php $ Listening on 127.0.0.1:4444... |
From different terminals
1 |
$ telnet 127.0.0.1 4444 |
If you will look into server.php you will find:
1 |
$server = new \Sock\SocketServer(); |
By default SocketServer listens on port 4444 but it can be changed with an argument:
1 |
$server = new \Sock\SocketServer(5555); |
1 |
$server->init(); |
Create socket resource.
1 |
$server->setConnectionHandler( 'onConnect' ); |
Every connection will be handled by onConnect function. It can be found at the beginning of server.php.
1 |
$server->listen(); |
Listen for clients.
Fork is called at the beginning of onConnect().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function onConnect( $client ) { $pid = pcntl_fork(); if ($pid == -1) { die('could not fork'); } else if ($pid) { // parent process return; } $read = ''; printf( "[%s] Connected at port %d\n", $client->getAddress(), $client->getPort() ); while( true ) { $read = $client->read(); |
When pcntl_fork() is called application splits into two simultaneous processes. We want child to handle the connection and parent to return from the function. In order to do that we need to know which is which. In fork manual (http://php.net/manual/en/function.pcntl-fork.php) you can find that parent process will get child’s PID while for child pcntl_fork() will return 0.
Rest of the function it’s just a simple code to allow some interaction.
This is how to handle multiple connections from a CLI script. In next post I’m going to show how to communicate between processes to create a simple chat server.
1 Comment
Joubert
21/08/2013Hi, I liked your tutorial and classes on github so much.
I have one question.
I’m using a server for multiple connections, when a connection is stabilized, my device send a data normally, but after that, the server is waiting for the device to send more data, if I try to send a command to the device, the command isn’t sent to it while it doesn’t send anything to server.
Is there any way for me to send the command in real time once the connection is stable and there is no data being sent?