Developing an Extensible TCP Server with Sockets in PHP

Are you interested in learning how to manipulate low-level sockets in PHP? Your search has finished. This is the second part of the series “Handling sockets in PHP,” and hopefully you’ll find in it valuable material regarding the creation and manipulation of sockets with PHP, in conjunction with numerous illustrative hands-on examples that will help you build socket servers in a few easy steps.

Introduction

For those who haven’t read my first tutorial yet, let me briefly recapitulate the topics that I treated previously. To begin with, I explained how to create a low-level socket in PHP and perform some key operations with it, such as pushing in and pulling out data streams.

Aside from covering some key points related to handling sockets, I set up a complete example, which focused on developing a simple TCP server and the corresponding client. In this way I demonstrated how they can communicate with each other, introducing some kind of basic processing on the traveling data.

After testing the sample TCP server that I developed in the first article, surely you’ll realize the excellent capabilities that come with PHP for handling sockets, and more generally for addressing common issues associated with network programming. Going deeper into socket manipulation, building end-to-end communication channels as part of an application allows you to understand much better the logic behind the relationship between server and clients. Considering this crucial concept, programming sockets in PHP can be a useful experience.

Now, focusing specifically on this second article of the series, I’ll go one step further in the manipulation of low-level sockets, and show you how to expand the TCP server that was constructed previously, in order to provide it with the ability to attend to multiple requests coming from the same client. Hopefully, this experience will help you to get a more intimate knowledge of socket handling in PHP.

With the preliminaries out of the way, let’s get started.

{mospagebreak title=A quick look at the previous TCP server}

Before I show you how to expand the sample TCP server developed in the first tutorial, first allow me list the full source code that creates it, including the procedural script, the “createSocketServer()” function that I defined before, and finally the “SocketServer” class. Here are the corresponding signatures for each case:

/*
//**************************************************************
// procedural script for building a TCP server
//**************************************************************
*/
// define  TCP port & local host
$port=1234;
$host=’127.0.0.1′;
set_time_limit(0);
// create low level socket
if(!$socket=socket_create(AF_INET,SOCK_STREAM,0)){
    trigger_error(‘Error creating new socket’,E_USER_ERROR);
}          
// tie up socket to TCP port
if(!socket_bind($socket,$host,$port)){
    trigger_error(‘Error binding socket to TCP port’,E_USER_ERROR);
}
// begin listening connections
if(!socket_listen($socket)){
    trigger_error(‘Error listening socket
connections’,E_USER_ERROR);
}
// create communication socket
if(!$comSocket=socket_accept($socket)){
    trigger_error(‘Error creating communication
socket’,E_USER_ERROR);
}
// read socket input
$socketInput=socket_read($comSocket,1024);
// convert to uppercase socket input
$socketOutput=strtoupper(trim($socketInput)).”n”;
// write data back to socket server
if(!socket_write($comSocket,$socketOutput,strlen($socketOutput)))
{
    trigger_error(‘Error writing socket output’,E_USER_ERROR);
}
// close sockets
socket_close($comSocket);
socket_close($socket);

*/
//**************************************************************
// user-defined function for building a TCP server
//**************************************************************
/*
function createSocketServer($host=’127.0.0.1′,$port=1234){
    if(!preg_match(“/^d{1,3}.d{1,3}.d{1,3}.d{1,3}
$/”,$host)){
        trigger_error(‘Invalid IP address format.’,E_USER_ERROR);
    }
    if(!is_int($port)||$port<1||$port>65535){
        trigger_error(‘Invalid TCP port number.’,E_USER_ERROR);
    }
    set_time_limit(0);
    // create low level socket
    if(!$socket=socket_create(AF_INET,SOCK_STREAM,0)){
        trigger_error(‘Error creating new socket.’,E_USER_ERROR);
    }
    // bind socket to TCP port
    if(!socket_bind($socket,$host,$port)){
        trigger_error(‘Error binding socket to TCP
port.’,E_USER_ERROR);
    }
    // begin listening connections
    if(!socket_listen($socket)){
         trigger_error(‘Error listening socket
connections.’,E_USER_ERROR);
    }
    // create communication socket
    if(!$comSocket=socket_accept($socket)){
        trigger_error(‘Error creating communication
socket.’,E_USER_ERROR);
    }
    // read socket input
    $socketInput=socket_read($comSocket,1024);
    // convert to uppercase socket input 
    $socketOutput=strtoupper(trim($socketInput)).”n”;
    // write data back to socket server
    if(!socket_write($comSocket,$socketOutput,strlen
($socketOutput))){
        trigger_error(‘Error writing socket
output’,E_USER_ERROR);
    }
    // close sockets
    socket_close($comSocket);
    socket_close($socket);
}

*/
//**************************************************************
// PHP class for building a TCP server
//**************************************************************
*/
class SocketServer{
    var $host;
    var $port;
    function  SocketServer($host=’127.0.0.1′,$port=1234){
        if(!preg_match(“/^d{1,3}.d{1,3}.d{1,3}.d{1,3}
$/”,$host)){
            trigger_error(‘Invalid IP address
format.’,E_USER_ERROR);
        }
        if(!is_int($port)||$port<1||$port>65535){
            trigger_error(‘Invalid TCP port
number.’,E_USER_ERROR);
        }
        $this->host=$host;
        $this->port=$port;
        $this->connect();
    }
    function connect(){
        set_time_limit(0);
        // create low level socket
        if(!$socket=socket_create(AF_INET,SOCK_STREAM,0)){
            trigger_error(‘Error creating new
socket.’,E_USER_ERROR);
        }
        // bind socket to TCP port
        if(!socket_bind($socket,$this->host,$this->port)){
            trigger_error(‘Error binding socket to TCP
port.’,E_USER_ERROR);
        }
        // begin listening connections
        if(!socket_listen($socket)){
            trigger_error(‘Error listening socket
connections.’,E_USER_ERROR);
        }
        // create communication socket
        if(!$comSocket=socket_accept($socket)){
            trigger_error(‘Error creating communication
socket.’,E_USER_ERROR);
        }
        // read socket input
        $socketInput=socket_read($comSocket,1024);
        // convert to uppercase socket input 
        $socketOutput=strtoupper(trim($socketInput)).”n”;
        // write data back to socket server
        if(!socket_write($comSocket,$socketOutput,strlen
($socketOutput))){
            trigger_error(‘Error writing socket
output’,E_USER_ERROR);
        }
        // close sockets
        socket_close($comSocket);
        socket_close($socket);
    }
}

Right, as you can see above, I listed all the source code corresponding to both procedural and object-oriented approaches, in order to create a sample TCP server. However, as you’ll recall, all these examples demonstrate how to build a socket server that processes one request at a time; it is unable to handle multiple petitions.

Bearing in mind this limitation, in the next section I’ll show you how to expand the pertinent TCP server in such a way that it will be able to process multiple client requests.

To learn how this will be done, please keep on reading.

{mospagebreak title=Expanding the original TCP server: processing multiple client requests}

Once you’ve learned how to create a low-level socket in PHP, in conjunction with reading and writing socket data, expanding the previous TCP server is a fairly standard process. Basically I’m not going to introduce new terms or concepts to achieve the new desired functionality.

In short, what I’ll do next is change the original procedural script and introduce a simple loop structure, in order to provide the server with the ability to keep running over and over again, attending to multiple client requests, until it is deliberately stopped.

Here is the source code for the improved TCP server, now capable of processing many requests:

// define  TCP port & local host
$port=12345;
$host=’127.0.0.1′;
set_time_limit(0);
// create low level socket
if(!$socket=socket_create(AF_INET,SOCK_STREAM,0)){
    trigger_error(‘Error creating new socket’,E_USER_ERROR);
}
// tie up socket to TCP port
if(!socket_bind($socket,$host,$port)){
    trigger_error(‘Error binding socket to TCP
port’,E_USER_ERROR);
}
// begin listening connections
if(!socket_listen($socket)){
    trigger_error(‘Error listening socket
connections’,E_USER_ERROR);
}
// create communication socket
if(!$comSocket=socket_accept($socket)){
    trigger_error(‘Error creating communication
socket’,E_USER_ERROR);
}
$message=’This is a simple TCP/IP server created with PHP
sockets!’.”rn”;
socket_write($comSocket,$message,strlen($message));
// start a loop and continue reading user input
do{
    // delay loop execution
    sleep(10);
    // read socket input
    $socketInput=socket_read($comSocket,1024);
    if(trim($socketInput)!=”){         
        // if user did not entered the ‘STOP” command continue
reading data
        if(trim($socketInput)!=’STOP’){
            // build socket output 
            $socketOutput=’The string you entered was
‘.$socketInput.”rn”;
            // write data back to socket server
            socket_write($comSocket,$socketOutput,strlen
($socketOutput));
            echo ‘The string you entered is ‘.$socketOutput;
        }
        else{
            // if ‘STOP’ command was entered close communication
socket & terminate all the connections
            socket_close($comSocket);
            break;
        }
    }
}
while(true);
// close global socket
socket_close($socket);

Even though the script shown above is closely similar to the one I wrote in the first part of this series, it also exposes some differences worth mentioning. First, please notice the inclusion of the “do-while” loop, in order to keep the server listening over and over for incoming client connections.

Second, the server will display a simple welcome message, and will only stop listening for requests when the client sends out the “STOP” command. If this happens, the loop is halted and both the general and communication sockets are properly closed.

Additionally, in this example, I slightly modified the behavior of the server, so each time the client transmits a string, it will be redisplayed to the client. Of course, there’s plenty of room to experiment here, and certainly you may want to change the source code in order to make the server perform a more useful task.

Now, take a look at the following screen shots, which depict the whole client-server interaction process using a Microsoft Telnet client:

As you can see on the above images, after starting the server from the PHP command line, I used a simple Telnet client (like Microsoft’s) and made some requests to it, by entering different strings. In all cases, the server displayed the inputted string in the console, and only stopped working when I entered the “STOP” command.

In this example, hopefully you could see how data comes in and out through the communication channel established between the server and the client. Simple and educational, isn’t it?

At this point, I hope you’ve already grasped the driving logic for creating a TCP server, which now processes multiple requests from a specific client, by using some socket programming PHP functions.

Therefore, come with me and read the last section of the article, in order to learn how to translate the procedural script you saw before into a compact and reusable function, and an additional PHP class.

{mospagebreak title=Defining the createSocketServer() function and the SocketServer class}

Since I already showed you how to use a procedural script for creating a multi-request TCP server, I’ll now list different versions of the same application, just in case you want to work with a reusable function or, specifically in the object-based area, with a PHP class. Here are the respective definitions for both approaches:

/*
/**************************************************************
/ ‘createSocketServer()’ function
/**************************************************************
*/
function createSocketServer($host=’127.0.0.1′,$port=12345){
    set_time_limit(0);
    // create low level socket
    if(!$socket=socket_create(AF_INET,SOCK_STREAM,0)){
        trigger_error(‘Error creating new socket’,E_USER_ERROR);
    }
    // tie up socket to TCP port
    if(!socket_bind($socket,$host,$port)){
        trigger_error(‘Error binding socket to TCP
port’,E_USER_ERROR);
    }
    // begin listening connections
    if(!socket_listen($socket)){
        trigger_error(‘Error listening socket
connections’,E_USER_ERROR);
    }
    // create communication socket
    if(!$comSocket=socket_accept($socket)){
        trigger_error(‘Error creating communication
socket’,E_USER_ERROR);
    }
    $message=’This is a simple TCP/IP server created with PHP
sockets!’.”rn”;
    socket_write($comSocket,$message,strlen($message));
    // start a loop and continue reading user input
    do{
        // delay loop execution
        sleep(10);
        // read socket input
        $socketInput=socket_read($comSocket,1024);
        if(trim($socketInput)!=”){     
            // if user did not entered the ‘STOP” command
continue reading data
            if(trim($socketInput)!=’STOP’){
                // convert to uppercase socket input 
                $socketOutput=’The string you entered was
‘.$socketInput.”rn”;
                // write data back to socket server
                socket_write($comSocket,$socketOutput,strlen
($socketOutput));
                echo ‘The string you entered is ‘.$socketOutput;
            }
            else{
                // if ‘STOP’ command was entered close
communication socket & terminate all the connections
                socket_close($comSocket);
                break;
            }
        }
    }
    while(true);
    // close global socket
    socket_close($socket);
}

/*
/*************************************************************
/  ‘SocketServer()’ class
/*************************************************************
*/
class SocketServer{
    var $host;
    var $port;
    var $delay=10;
    var $message=”This is a simple TCP/IP server created with PHP
sockets!rn”;
    function SocketServer($host=’127.0.0.1′,$port=12345){
        if(!is_int($port)||$port<1||$port>65535){
            trigger_error(‘Invalid port number’,E_USER_ERROR);
        }
        $this->host=$host;
        $this->port=$port;
        $this->connect();
    }
    function connect(){
        set_time_limit(0);
        // create low level socket
        if(!$socket=socket_create(AF_INET,SOCK_STREAM,0)){
            trigger_error(‘Error creating new
socket’,E_USER_ERROR);
        }
        // tie up socket to TCP port
        if(!socket_bind($socket,$this->host,$this->port)){
            trigger_error(‘Error binding socket to TCP
port’,E_USER_ERROR);
        }
        // begin listening connections
        if(!socket_listen($socket)){
            trigger_error(‘Error listening socket
connections’,E_USER_ERROR);
        }
        // create communication socket
        if(!$comSocket=socket_accept($socket)){
            trigger_error(‘Error creating communication
socket’,E_USER_ERROR);
        }
        socket_write($comSocket,$this->message,strlen($this-
>message));
        // start a loop and continue reading user input
        do{
            // delay loop execution
            sleep($this->delay);
            // read socket input
            $socketInput=socket_read($comSocket,1024);
            if(trim($socketInput)!=”){ 
                // if user did not entered the ‘STOP” command
continue reading data
                if(trim($socketInput)!=’STOP’){
                    $socketOutput=’The string you entered was
‘.$socketInput.”rn”;
                    // write data back to socket server
                    socket_write($comSocket,$socketOutput,strlen
($socketOutput));
                    echo ‘The string you entered is
‘.$socketOutput;
                }
                else{
                    // if ‘STOP’ command was entered close
communication socket & terminate all the connections
                    socket_close($comSocket);
                    break;
                }
            }
        }
        while(true);
        // close global socket
        socket_close($socket);
    }
}

In the first case, if you’re going to use the “createSocketServer()” function, this one can be used like this:

// call ‘createSocketServer()” function
createSocketServer();

Or, in case you prefer to work with an object-oriented approach, the “SocketServer” class can be utilized as follows:

// instantiate ‘SocketServer’ object
$sock=&new SocketServer();

To wrap up

That’s all for the moment. In this second part of the series, I’ve shown you how to build an extensible TCP server that handles multiple incoming requests, using the PHP socket-related functions that you learned in the first tutorial. But, do you think that’s all? Nope; in the last article, I’ll demonstrate how to implement a Web server on your own machine, by utilizing a few low-level sockets. You don’t have any excuses to miss it!

[gp-comments width="770" linklove="off" ]

chat