Simple Socket Server


We start coding the socket server with a few pragmatic concerns.

HTML
 <html><head><title>SERVER OF SOCKETS</title></head><body> 

It is good to have a clear title on the window. When we are debugging a multiple-process system like this, many windows are open at once. It is important to easily identify the significant windows even in a crowded display.

Unix Error Codes

PHP
 <?php function check( $value, $function ) {    if( $value >= 0 )         print( "\ n<br/>function [ $function ] returns $value.(success)");    else         die  ( "\ n<br/>function [ $function ] returns $value.                    (<I><B>FAILURE:</B></I> --".strerror($value)."---)" );     superflush(); } 

This check() function simply handles our response to the many system-level function calls we make in this program. Some return integers as handles or lengths. Others use zero as a return code to indicate success. But they universally use negative numbers to indicate errors and they share a single mapping of negative numbers to error messages.

Throughout our software we call check() with the result (and name ) of a critical function as it is performed. If things go well, we get a success message on our browserwhich is serving as monitor. We use both <br/> and \n to force a newline in both HTML and a straight dump. We use print() rather than echo because we can flush its output buffer. Nevertheless, a more powerful flush is required, called superflush().

If there is a problem, it is reported and the program aborts ( die provides both functions). The function strerror() translates a numeric errorcode into a human-ready error message.

Buffer Control

 function superflush() {   for($i=3;$i;$i--){        for($j=5;$j;$j--)             print("                                         ");        flush();        } } 

This is not very lovely. Neither is the rubber plunger standing near a balky drain. But both are necessary tools.

The PHP call to flush() was developed to override the normal buffering of the web server system that causes output to accumulate before it is transmitted back and displayed at the client. Normally, the buffering makes the system more efficient. But it delays the display of tiny fragments such as the debugging and progress messages we are looking for.

Ideally, a single call to flush() would be enough to push the output we want to the screen. But there are several steps between the code and the client screen where buffering is performed. Pushing a lot of inert whitespace through the system helps clear its buffers.

Trial and error proved that this brute force superflush() is required. Its details are definitely system dependent. Some systems need only the normal flush(). Others might need an ultramegasuperflush().

HTTP Channel

This flush is used to clear the HTTP channel (the normal browser/server web channel), not the socket channel that directly connects Flash and PHP. Right now we use the HTTP channel only to open a browser window for development purposes. Using simple HTML, we can monitor the PHP program as clients connect and communicate.

Later we might use the channel more strategically. For example, we discussed how the HTTP channel could provide a Flash client with the port number of the desired service. This lets us keep the port number pretty dynamic, both from installation to installation and on the same machine.

The HTTP channel can serve to launch this whole process. This socket server is an ordinary PHP script and is launched in the ordinary way by the web server responding to a normal HTTP request from a browser or a Flash app. Using a simple browser, we can launch and monitor the server remotely across the web. Or we can let our Flash users automatically fire up the service as needed.

Cleanup

PHP
 function closedown() { global $sock, $msgsock;     echo( "</body></html>" );     close ($msgsock);     close ($sock); } 

PHP does not use many event handlers or callback functions. That is a shame in this caseit will be clear shortly that the event-driven socket code we wrote in ActionScript is a far better system than the polling loop we will code in PHP. The event-driven code is cleaner, and the software performs more efficiently and reliably.

We do have a callback function that handles cleanup when the PHP script terminates. This cleanup code originally simply sat at the end of the script. By registering it as the shutdown function we can be sure that it is also called when the script is aborted for any of a variety of reasons.

Time Limit

PHP
 set_time_limit(120);   register_shutdown_function ("closedown" );   $address = gethostbyname ($SERVER_NAME);   $port =    12539; 

One reason for the script to abort is a time-out. We give this script a 2-minute (120-second) time limit while it is on the workbench. In actual service, we will want a longer (or unlimited) time, but while we are developing code that can monopolize system resources, we want a safety valve to prevent a meltdown.

Figure 19.3. The Two Steps of a Client/Server Connection

graphics/19fig03.jpg

The final lines install our cleanup code, retrieve our IP address, and choose an arbitrary port number.

Listen Socket

PHP
 print( "Opening a socket at <b>".$address.' : '.$port."</b>.");   check( $sock =      socket(AF_INET,SOCK_STREAM,0),  "socket" );   check( $ret  =      bind  ($sock, $address, $port), "bind"   );   check( $ret =       listen($sock, 5)              , "listen" ); 

The listening socket, $sock, is built in three steps:

  1. The function socket() establishes the handle for a socket and allocates the data structure that will manage it. The AF_INET parameter specifies an Internet socket (as opposed to a Unix socket for communication between local processes). SOCK_STREAM specifies a data stream rather than several more primitive and fragmented options.

  2. Function bind() associates our named handle with a unique network address. It fails if that host/port/protocol combination is already in use. If we are sloppy in our allocations and shutdowns, this will be a problem. We can leave ports unavailable even after our code has completed. This is especially a problem during development.

  3. Finally we tell $sock to listen to the port. The listen() function does not wait for data. Rather, it waits for connection requests . That is the sole purpose of this socket ( $sock ): It lingers at this port and welcomes incoming clients. We are, in this case, allowing it to build a queue of up to five pending clients.

These requests must then be processed .

Accepting a Connection

PHP
 set_nonblock ($sock );  while( $msgsock<1 ){    echo strerror( $msgsock )."<br/>";    superflush();    $msgsock =   accept_connect($sock);    sleep(1);    } 

By setting nonblock mode on this socket, we know that the execution of our script will not stop when we call accept_connect() . Were it in blocking mode, the script would wait for the accept_connect() action to complete. If nobody is waiting to connect, our process comes to a halt.

Our example has little practical advantage over a blocking strategy. The behavior is similar, but our nonblocking example is a little more lively and its action can be more visible. While it loops around waiting for accept_connect() to be satisfied, the script can print a status message (Figure 19.4).

Figure 19.4. Waiting for a Connection, Verbose Style

graphics/19fig04.gif

The significant line of code is the one that creates the new socket $msgsock by accepting a connection from $sock. These two sockets are not equivalent.

The original $sock socket goes back immediately to its job of waiting for new customers. In this single- user service this is not significant. In multiplexed systems, it will generate more message sockets to maintain more client connections.

The new socket $msgsock is associated with a particular user of the service, whereas $sock is associated with the public interface to the service. Unlike $sock, $msgsock conducts message traffic. We cannot use $msgsock to listen for new connections, and we cannot use $sock to transfer data.

Message Socket

PHP
 set_nonblock ($msgsock );   do {     $buf="";     $readerr      =  read( $msgsock, $buf, 48);     if( $buf )         $writeerr = write( $msgsock, strtoupper($buf), strlen($buf) );     }   while( !stristr( $buf, "quit") );   closedown(); ?> 

The actual web service happens in this loop. We put $msgsock to work. It is not dramatic work. This example is just an echo service: It repeats back to the client each string it receives from the client.

The nonblock mode means that the loop progresses quickly. If there is data to be read, it is read; if not, the operation is skipped .

The buffer is emptied, and we attempt to refill it with a read operation. If the buffer has been filled, it is written back to the socket. To leave our process's footprint on the string, the script forces the text to uppercase.

Finally, we actually examine the user's message. If the user writes "quit", we do (Figure 19.5).

Figure 19.5. Disconnection

graphics/19fig05.jpg

NOTE

At the time of this writing, we were using a version of PHP (4.0.4) where the nonblock read function was broken. Our software still worked without this feature but awkwardly. It made some compromises attractive, such as the fixed-length reads.

Later, in our example of multiplexed sockets, it made our software very clumsy. This bug in PHP was known and has been scheduled to be addressed, so it is hopefully now corrected and we can perform socket reads in nonblocking mode.




Flash and XML[c] A Developer[ap]s Guide
Flash and XML[c] A Developer[ap]s Guide
ISBN: 201729202
EAN: N/A
Year: 2005
Pages: 160

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net