Emulating a Basic Web Server with Sockets in PHP

Do you think that programming sockets with PHP is really hard work? Not at all. If you’re still not convinced, read this final part of the series “Handling sockets with PHP.” In three parts, this series shows how to work with low-level sockets in PHP. It introduces some of the typical tasks, such as creating sockets, as well as reading and writing socket data.

Introduction

For those who haven’t yet read the second tutorial of this series, allow me to describe in a few words the topics I treated in it. Since using sockets in PHP is a pretty straightforward experience, in the previous article I explained how to create a basic TCP server, which was capable of handling multiple requests coming from the same client application. In this way I eliminated a considerable limitation exposed in the first article.

Despite the rather primitive functionality of the mentioned TCP server, I hope the example I developed before served as a way for you to get a more intimate grounding in how to program low-level sockets in PHP, and of course contributed to expanding your general knowledge of PHP programming.

Also, this sample TCP server used some of the typical socket-related PHP functions, in order to create the corresponding end-point communication channels, as well as handling streams of data for reading and writing client input. With reference to demonstrating how to get the server working, I used Microsoft’s Telnet application included in Windows XP, but because of the versatility of the TCP server, you can use your own client program without having to modify the original socket application.

Having reminded you of the key points regarding the sample TCP server I created in the previous tutorial, it’s time to pay attention to the subject of this final part of the series. In this article, I’ll show you how to use low-level sockets to emulate the functionality of a basic Web server on your own testing machine.

Want to see how this server will be created? Fine, let’s do it together!

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

Before I start coding the Web server, I’d like you to recall how the previous TCP server was developed. Since the Web server I plan to build will use most of the PHP functions that you learned in the previous installments, you shouldn’t have any problems understanding how it will work.

That said, here is the full source code that corresponds to the previous TCP server, including both procedural and object-oriented approaches:

/*
/****************************************************************
/ procedural script for building a multi-request TCP server
/****************************************************************
*/

// 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 comunication 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);

/*
/****************************************************************
/ ‘createSocketServer()’ function for building a multi-request
TCP server
/****************************************************************
*/
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 for creating a multi-request TCP server
/**************************************************************
*/
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);
    }
}

Right, as you can see, I listed above the complete source code for implementing a multi-request TCP server, including a raw procedural script, and of course adding the corresponding “createSocketServer()” function and the “CreateSocket” PHP class. Whether you’re using a procedural approach or an object-based method for coding your applications, I’m sure that one of the above listed implementations of the pertinent TCP server will meet your particular needs.

Now that I reminded you of how a multi-request TCP server can be created, join me in the next section, in order to learn how to emulate a basic Web server on your own testing machine. Go ahead and read the next section.

{mospagebreak title=Emulating a Web server}

Emulating the behavior of a real Web server with low-level sockets is a quite easy process to perform. In this case, I’m not going to use real HTTP commands like GET or POST requests, since it’d be rather pointless (probably you have a production-level Web server already installed on your computer), so instead I’ll create an “always-on” TCP server, which will be capable of fetching a specific (X)HTML file, according to the request made by a client.

At the end of this interaction, the contents of this file will be displayed to the user, in this way emulating the interactive process between a real Web server and the corresponding HTTP user agent.

Having explained how the Web server will work, here is the PHP class that precisely implements this application. Please, have a look at the code listed below:

// define ‘WebServer’ class
class WebServer {
    var $docDir;
    var $host;
    var $port;
    var $socket;
    var $comSocket;
    function WebServer($host=’127.0.0.1′,$port=’12345′,$docDir=’defaultDir’){
        $this->docDir=$docDir;
        $this->host=$host;
        $this->port=$port;
        $this->createSocket();
        $this->readUserInput();
    }
    function createSocket(){
        set_time_limit(0);
        // create low level socket
        if(!$this->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($this->socket,$this->host,$this->port)){
            trigger_error(‘Error binding socket to TCP
port’,E_USER_ERROR);
        }
        // begin listening connections
        if(!socket_listen($this->socket)){
            trigger_error(‘Error listening socket
connections’,E_USER_ERROR);
        }
        // create communication socket
        if(!$this->comSocket=socket_accept($this->socket)){
            trigger_error(‘Error creating communication
socket’,E_USER_ERROR);
        }
        // display welcome message
        $message=’Welcome to the Console Web Server! Please type
below the file name you want to fetch…’.”rn”;
        socket_write($this->comSocket,$message,strlen($message));
    }
    // read user input
    function readUserInput(){
        // start a loop and continue reading user input
        do{
            // delay loop execution
            sleep(10);
            // read socket input
            $socketInput=socket_read($this->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=$this->fetchFile
($socketInput).”rn”;
                    // write data back to socket server
                    socket_write($this-
>comSocket,$socketOutput,strlen($socketOutput));
                }
                else{
                    // if ‘STOP’ command was entered close
communication socket & terminate all the connections
                    socket_close($this->comSocket);
                    break;
                }
            }
        }
        while(true);
        // close global socket
        socket_close($this->socket);
    }
    // fetch (X)HTML file
    function fetchFile($socketInput){
        $path=’C:\phpcli\’.$this->docDir.’\’.trim
($socketInput);
        if(!file_exists($path)){
            return ‘File not found on this server. Please try
again';
        }
        return file_get_contents($path);
    }
}

As you can see, the above class, which I have amazingly called “WebServer,” implements a basic Web server by using some of the PHP socket-related functions you saw in previous tutorials. Its constructor takes up three arguments: the host to which a connection is established, the TCP port used by the server and the client in conjunction, and finally the physical directory where the server will fetch all the requested files.

In order to create a low-level socket and read/write data streams, the class additionally exposes the “createSocket()” method, which also calls the private “readUserInput()” method, handy for reading the name of the file being fetched. Finally, the “fetchFile()” method is tasked with retrieving the contents of the file specified by the client.

Before I proceed further, I’d like to clarify one important point: notice that the “fetchFile()” method uses the predefined “C:phpcli” path, in order to fetch all the requested files. In this case, the server will be run on a Windows system, but if you’re working with Linux or some other operating system, you may want to change this directory and fetch files from another location.

Having addressed the topic mentioned before, it’s time to put this Web server to work. Please click on the link below to see an example that shows how the server will retrieve files requested by a Telnet client.

{mospagebreak title=Putting the Web server to work}

In order to see how the Web server works, first I’ll define an example “index.htm” web page, which will be saved to the default directory specified in the “WebServer” class. Below, I listed this sample web document:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html>
<head>
<title>TEST WEB DOCUMENT</title>
<meta http-equiv=”Content-Type” content=”text/html; charset=iso-
8859-1″ />
</head>
<body>
<h1>THIS IS AN EXAMPLE WEB PAGE</h1>
</body>
</html>

After defining the above (X)HTML file, the Web server will be run by a “webserver.php” file, which obviously will contain the corresponding definition of the “WebServer” class and use an instance of it, like this:

// instantiate ‘WebServer’ object
$webServer=&new WebServer();

Now that both “index.htm” and “webserver.php” files have been defined respectively, take a look at the following screenshots, which illustrate how to use the Web server by utilizing a Telnet program as the client :

As shown above, the first screenshot depicts the process where the “index.htm” file is requested and then displayed. The second image shows the response of the server when trying to retrieve a non-existent file, as well as when the server is stopped by entering the STOP command.

Of course, I think this Web server won’t be capable of beating Apache, but the above example is enough to demonstrate the powerful capabilities of low-level sockets in PHP.

Final thoughts

Finally, we’re done. In this new PHP series, you learned how to work with sockets in PHP through the development of different examples, such as creating a TCP server, and more specifically building an application that emulates a basic Web server.

In all the code samples, I used some of the most common PHP socket-related functions, therefore you shouldn’t have any problems using them as part of your own applications. See you in the next PHP tutorial!   

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

chat