Socket programming with streams in php

Socket programming involves connecting to remote machines on LAN or over the internet using ip addresses and port number. For example google.com has an ip "173.194.36.3" and runs http server on port 80. So a socket application can connect to that ip address on that particular port number and start data communication.

Socket extension

Php comes with a socket extension which provide c-style socket functions and can be used to write socket programs. However the socket extension is not the only option to do socket programming in php. Another technique is to use streams, like file streams. First lets take a look at how the socket extension code looks like.


if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0)))
{
	perror("Could not create socket");
}
echo "Socket created n";

//Connect socket to remote server
if(!socket_connect($sock , '74.125.235.20' , 80))
{
	perror("Could not connect");
}
echo "Connection established n";

$message = "GET / HTTP/1.1rnrn";
//Send the message to the server
if( ! socket_send ( $sock , $message , strlen($message) , 0))
{
    perror("Could not send data");
}
echo "Message send successfully n";

//Now receive reply from server
if(socket_recv ( $sock , $buf , 500 , MSG_WAITALL ) === FALSE)
{
    perror("Could not receive data");
}
echo $buf;

///Function to print socket error message 
function perror($msg)
{
	$errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);
    
    die("$msg: [$errorcode] $errormsg n");
}

The above program connects to google.com ip address on port number 80 and then sends http request and receives the output. The functions called are socket_create, socket_connect, socket_send and socket_recv.

The same program can be implemented in a bit different style using file streams.

Streams

Lets take a look.

$fp = fsockopen("www.google.com", 80, $errno, $errstr, 30);

if (!$fp) 
{
	echo "$errstr ($errno)<br />n";
} 
else 
{
	$out = "GET / HTTP/1.1rnrn";
	///Send data
	fwrite($fp, $out);
	
	///Receive data - in small chunks :)
	while (!feof($fp)) 
	{
		echo fgets($fp, 128);
	}
	
	fclose($fp);
}

The fsockopen function creates the socket and the connection together. The fwrite function sends data and fread receives data. Simple and similar to reading/writing from file streams. The above example creates a tcp socket. To create udp socket the socket stream wrapper has to be specified like this







$fp = fsockopen("udp://127.0.0.1", 13, $errno, $errstr);

The stream wrapper for tcp is ofcourse tcp://
The general syntax for the socket url is

$protocol://$host:$port

The above style is quick and easy alternative to the socket extension. And it does not require any extra extension and works inside php. There is another function called stream_socket_client that can do the same thing as fsockopen

$fp = stream_socket_client("tcp://www.google.com:80", $errno, $errstr, 30);
if (!$fp) 
{
	echo "$errstr ($errno)<br />n";
} 

The socket url structure is same. Only that the port number does not have an extra parameter.
That much is all about writing socket clients. Next thing to code is socket servers. Programs that accept incoming socket connections and respond to their requests.

Socket Servers

$server = stream_socket_server("tcp://0.0.0.0:4444", $errno, $errorMessage);

if ($server === false) 
{
	throw new UnexpectedValueException("Could not bind to socket: $errorMessage");
}

for (;;) 
{
	$client = stream_socket_accept($server);
	
	if ($client) 
	{
		echo 'Connection accepted from '.stream_socket_get_name($client, false) . "n";
		
		stream_copy_to_stream($client, $client);
		fclose($client);
	}
}

Run the above php script to start the socket server. Start another terminal and connect to this server.

$ telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
sd
sd

The server acts as a simple echo server, by sending back the same message it receives. This server has a limitation, that it cannot handle multiple connections. Once a client connects to it, then it just serves that particular client.

Handle multiple clients

<?php

// open a server on port 4444
$server = stream_socket_server("tcp://0.0.0.0:4444", $errno, $errorMessage);

if ($server === false) 
{
	die("Could not bind to socket: $errorMessage");
}

$client_socks = array();
while(true)
{
	//prepare readable sockets
	$read_socks = $client_socks;
	$read_socks[] = $server;
	
	//start reading and use a large timeout
	if(!stream_select ( $read_socks, $write, $except, 300000 ))
	{
		die('something went wrong while selecting');
	}
	
	//new client
	if(in_array($server, $read_socks))
	{
		$new_client = stream_socket_accept($server);
		
		if ($new_client) 
		{
			//print remote client information, ip and port number
			echo 'Connection accepted from ' . stream_socket_get_name($new_client, true) . "n";
			
			$client_socks[] = $new_client;
			echo "Now there are total ". count($client_socks) . " clients.n";
		}
		
		//delete the server socket from the read sockets
		unset($read_socks[ array_search($server, $read_socks) ]);
	}
	
	//message from existing client
	foreach($read_socks as $sock)
	{
		$data = fread($sock, 128);
		if(!$data)
		{
			unset($client_socks[ array_search($sock, $client_socks) ]);
			@fclose($sock);
			echo "A client disconnected. Now there are total ". count($client_socks) . " clients.n";
			continue;
		}
		//send the message back to client
		fwrite($sock, $data);
	}
}

The stream_select function takes arrays of socket to check for reading, writing or errors.

stream_select ( $read_socks, $write, $except, 300000 )

Whenever a certain event occurs on any of the sockets, stream_select will return and each array will be filled with the sockets that had events. For example if one of the read sockets received a message from client then select will fill the read_socks array with the socket and return. Similarly incoming new connections will also cause stream_select to return. In this case we do not have anything in the write and except socket arrays.

$ php /var/www/socket.php 
Connection accepted from 127.0.0.1:42203
Now there are total 1 clients.
Connection accepted from 127.0.0.1:42204
Now there are total 2 clients.
Connection accepted from 127.0.0.1:42205
Now there are total 3 clients.
A client disconnected. Now there are total 2 clients.
A client disconnected. Now there are total 1 clients.
A client disconnected. Now there are total 0 clients.

The above output of the server console shows how 3 clients connected and then disconnected away. That much being on sockets for now, check out the manual pages for more information.

Last Updated On : 23rd March 2013

Subscribe to get updates delivered to your inbox

2 Comments + Add Comment

  • Hey ! Nice article ,
    I have used this , but I am stuck at 256 clients connections but thats the limit I cna not expand that,
    I am stuck there , can you help ?

    I am using windows server.

  • nice! I’m experimenting with proc_open, set_stream_blocking and stream_select with a python script that takes about a minute to process. trying to find a way to display output from the python script to the browser as it comes.

Leave a comment