Sep/0815
Creating a PHP socket server
Socket server’s are great things for creating multiuse applications. Sockets are the most basic and thus efficient and quickest way for web applications Java, Flash, PHP, etc. to exchange data with eachother. With this you can create online multiplayer games, chat applications, mail and web servers, etc. Basically the only thing holding you back is your imagination. Most web dev’s however, don’t know how to create or use one. Well I found a way of creating a socket server with PHP. There’s not a lot of info about this on the web, but it is contrary to what many think a legal, possible and quite reliable use of PHP. It is actually even covered on the Zend Developer Zone. The code below is mainly based on that tutorial, but this one actually works in PHP5 and has been debugged a little further.
You will need three things for doing this tutorial;
- a PHP server enviroment at hand, because you will need to use your command line interface (don’t get affraid now, it’s easier than making web-apps)
- a way of creating PHP scripts; notepad, dreamweaver, etc.
- (if you want your socket server to run online) a free internet connected socket, which can pass through router, firewall, and is not beeing used by any other programs.
This is the debugged code, explanation is mainly in the comments and below;
<?php
// PHP SOCKET SERVER
error_reporting(E_ERROR);
// Configuration variables
$host = "127.0.0.1";
$port = 4041;
$max = 20;
$client = array();
// No timeouts, flush content immediatly
set_time_limit(0);
ob_implicit_flush();
// Server functions
function rLog($msg){
$msg = "[".date('Y-m-d H:i:s')."] ".$msg;
print($msg."\n");
}
// Create socket
$sock = socket_create(AF_INET,SOCK_STREAM,0) or die("[".date('Y-m-d H:i:s')."] Could not create socket\n");
// Bind to socket
socket_bind($sock,$host,$port) or die("[".date('Y-m-d H:i:s')."] Could not bind to socket\n");
// Start listening
socket_listen($sock) or die("[".date('Y-m-d H:i:s')."] Could not set up socket listener\n");
rLog("Server started at ".$host.":".$port);
// Server loop
while(true){
socket_set_block($sock);
// Setup clients listen socket for reading
$read[0] = $sock;
for($i = 0;$i<$max;$i++){
if($client[$i]['sock'] != null)
$read[$i+1] = $client[$i]['sock'];
}
// Set up a blocking call to socket_select()
$ready = socket_select($read,$write = NULL, $except = NULL, $tv_sec = NULL);
// If a new connection is being made add it to the clients array
if(in_array($sock,$read)){
for($i = 0;$i<$max;$i++){
if($client[$i]['sock']==null){
if(($client[$i]['sock'] = socket_accept($sock))<0){
rLog("socket_accept() failed: ".socket_strerror($client[$i]['sock']));
}else{
rLog("Client #".$i." connected");
}
break;
}elseif($i == $max - 1){
rLog("Too many clients");
}
}
if(--$ready <= 0)
continue;
}
for($i=0;$i<$max;$i++){
if(in_array($client[$i]['sock'],$read)){
$input = socket_read($client[$i]['sock'],1024);
if($input==null){
unset($client[$i]);
}
$n = trim($input);
$com = split(" ",$n);
if($n=="EXIT"){
if($client[$i]['sock']!=null){
// Disconnect requested
socket_close($client[$i]['sock']);
unset($client[$i]['sock']);
rLog("Disconnected(2) client #".$i);
for($p=0;$p<count($client);$p++){
socket_write($client[$p]['sock'],"DISC ".$i.chr(0));
}
if($i == $adm){
$adm = -1;
}
}
}elseif($n=="TERM"){
// Server termination requested
socket_close($sock);
rLog("Terminated server (requested by client #".$i.")");
exit();
}elseif($input){
// Strip whitespaces and write back to user
// Respond to commands
/*$output = ereg_replace("[ \t\n\r]","",$input).chr(0);
socket_write($client[$i]['sock'],$output);*/
if($n=="PING"){
socket_write($client[$i]['sock'],"PONG".chr(0));
}
if($n=="<policy-file-request/>"){
rLog("Client #".$i." requested a policy file...");
$cdmp="<?xml version=\"1.0\" encoding=\"UTF-8\"?><cross-domain-policy xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd\"><allow-access-from domain=\"*\" to-ports=\"*\" secure=\"false\" /><site-control permitted-cross-domain-policies=\"master-only\" /></cross-domain-policy>";
socket_write($client[$i]['sock'],$cdmp.chr(0));
socket_close($client[$i]['sock']);
unset($client[$i]);
$cdmp="";
}
}
}else{
//if($client[$i]['sock']!=null){
// Close the socket
//socket_close($client[$i]['sock']);
//unset($client[$i]);
//rLog("Disconnected(1) client #".$i);
//}
}
}
}
// Close the master sockets
socket_close($sock);
?>
Basically this is a functioning socket server (execute it via PHPs CLI) but to actually do something with it requires some minor modifications. One easy modification you can do, is create a command to send a message to all other clients;
...if($n=="PING"){
socket_write($client[$i]['sock'],"PONG".chr(0));
}
You can already use this script with Flash, but only locally. I will soon write how to properly prepare this server for Flash (e.g. make it send policy files) and how to use it with flash. And for those who cannot wait for another post please remember that you cannot test your ‘internet’ (so not local) connecting flash client on the same PC as your PHP server.
1:43 pm on September 11th, 2008
I’m sorry for the lack of formatting in the source code, working on the problem. If anyone knows a good multi programming language plugin for WP with code formatting tell me…
6:32 pm on January 8th, 2009
Hi,
When i try to run the above script, i get the following
PHP Parse error: syntax error, unexpected $end in /home/aaa/soc.php on line 88
Any ideas why this could be happening ?
Thanks
4:15 pm on January 9th, 2009
I updated the entire script, seems like some brackets dissappaered in wordpress. It also includes a ping response, terminate command and the script will return a valid cross domain policy file to flash clients.
10:39 pm on February 4th, 2009
I had code quite similar to this working for quite a while (a couple of years). Then when I upgraded from 4.something to 5.2.6, it started giving a warning about
(xx) is not a valid resource
on the socket_select call. Other than that it continued to work as usual. But that bugged me.
After some debugging, I found out that the resource type for the client would sometimes change from ‘Socket’ to ‘Unknown’.
So now in the loop just before the socket_select, I check to see that the client resource is actually of type ‘Socket’, and if it is not, then I unset the $read[$i+1] if it is set, and voila, no more errors.
$read[0]=$sock;
for($i=0;$i<$max_clients;$i++){
if($client[$i]['sock']!=NULL&&is_resource($client[$i]['sock'])&&’Socket’==get_resource_type($client[$i]['sock']))$read[$i+1]=$client[$i]['sock'];
elseif(isset($read[$i+1]))unset($read[$i+1]);
}
$ready=socket_select($read,$write,$except,$tval);
9:13 am on February 5th, 2009
Thanks alot, I was allready wandering why that happened but was a bit too distracted lately to do anything about it
2:09 am on May 1st, 2009
Could you explain the
if(in_array($sock,$read))
line to me? Since
$read[0] = $sock;
is always executed, it seems like the if statement would always be true. Which, of course, means that even if there is not a new connection attempt, the script will try to accept a new connection.
I’m new to this so I’m probably just missing something, any help would be appreciated.
10:43 am on May 1st, 2009
I haven’t looked at this script in quite some time (actually wrote it a long time before posting it), but I think it’s there to check if the socket is still alive. Would the socket have shut down after $read[0] = $sock, $sock wouldn’t be the same and therefore not be found in the $read array. And this would probably prevent nasty errors in endless loops.
10:25 am on May 7th, 2009
hi.
can u post a simpe example using flash? for creating a multiplayer chat ou game?
12:17 pm on May 7th, 2009
Yes, I could, and I should. But I currently don’t have a lot of time on my hand (exam weeks…
) Try searching for some tutorials on using sockets in AS3, that should get you started
1:10 pm on May 7th, 2009
go and study!! i´m curious about your release of your 3D engine.
are you using quartennions?
All the best.
andre venancio
1:06 pm on May 8th, 2009
The 3D engine doesn’t use quaternions yet, in fact there is room for quite some improvement. Again, it’s on the to do list for after the exams
10:32 pm on January 26th, 2010
I’m curious why I need a command prompt. My host doesn’t give me a cmd prompt so I’m out of luck. But, since this is PHP, won’t it run when flash requests the PHP? i might be misguided. I found a Perl program that does the same thing but I can’t run that either without a cmd prompt.
5:26 pm on May 9th, 2010
I worked for me without cmd.
this is how:
upload to a server ( set the ip and port )
run the php from your browser the browser should start loading but the page should not come in (because of the while loop)
& connect with your flash…
5:01 pm on May 26th, 2010
Yes it’ll work fine, you can even let the log print to the browser if you set the implicit flush right and have no timeout.
11:03 pm on May 29th, 2010
Try this one :
http://kpumuk.info/projects/wordpress-plugins/codecolorer/