I've been thinking about using php socket functions to program a chat application that will work on the web. I'm new to socket programming, and so have been reading up on it.
I came across the code below. It is long, but simple for a socket guru like you to read.
The plan it as follows:
1. Clients connect to a listening server, and server adds client to a list (i.e. an array of client ip/port).
2. Whenever a client sends a message to the server, the server broadcasts that message to all clients on the list.
My question then is: is it possible for a php socket to send a message to a client on a known ip/port, without the client itself being a server?
<?php
set_time_limit(0);
// defaults...
define('MAXLINE', 1024); // how much to read from a socket at a time
define('LISTENQ', 10); // listening queue
$PORT = 10000; // the default port to run on
$FD_SETSIZE = 5; // file descriptor set size (max number of concurrent clients)...
// for kill the Daemon...
function killDaemon()
{
global $listenfd, $client, $FD_SETSIZE;
socket_close($listenfd);
$msg = "Daemon going down!\n";
for ($i = 0; $i < $FD_SETSIZE; $i++)
{
if ($client[$i] != -1)
{
socket_write($client[$i], $msg, strlen($msg));
socket_close($client[$i]);
}
}
print "Shutting down the daemon\n";
exit;
}
// whenever a client disconnects...
function closeClient($i)
{
global $allset, $client, $isAdmin, $remote_host, $remote_port;
print "closing client[$i]\n";
socket_fd_clear($allset, $client[$i]);
socket_close($client[$i]);
$client[$i] = -1;
unset($remote_host[$i]);
unset($remote_port[$i]);
}
// set up the file descriptors and sockets...
// $listenfd only listens for a connection, it doesn't handle anything
// but initial connections, after which the $client array takes over...
$allset = socket_fd_alloc();
$listenfd = socket_create(AF_INET, SOCK_STREAM, 0);
if ($listenfd)
print "Listening on port $PORT\n";
else
die("AIEE -- socket died!\n");
socket_setopt($listenfd, SOL_SOCKET, SO_REUSEADDR, 1);
if (!socket_bind($listenfd, "0.0.0.0", $PORT))
{
socket_close($listenfd);
die("AIEE -- Couldn't bind!\n");
}
socket_listen($listenfd, LISTENQ);
// set up our clients. After listenfd receives a connection,
// the connection is handed off to a $client[].
$maxi = -1;
for ($i = 0; $i < $FD_SETSIZE; $i++)
$client[$i] = -1;
socket_fd_zero($allset);
// the main loop.
while(1)
{
socket_fd_set($allset, $listenfd);
for ($i = 0; $i < $FD_SETSIZE; $i++)
{
if ($client[$i] != -1)
socket_fd_set($allset, $client[$i]);
}
// block indefinitely until we receive a connection...
$nready = socket_select($allset, null, null, null);
// if we have a new connection, stick it in the $client array,
if (socket_fd_isset($allset, $listenfd))
{
for ($i = 0; $i < $FD_SETSIZE; $i++)
{
if ($client[$i] < 0)
{
$client[$i] = socket_accept($listenfd);
socket_setopt($client[$i], SOL_SOCKET, SO_REUSEADDR, 1);
socket_getpeername($client[$i], $remote_host[$i], $remote_port[$i]);
print "Accepted {$remote_host[$i]}:{$remote_port[$i]} as client[$i]\n";
break;
}
if ($i == $FD_SETSIZE - 1)
{
trigger_error("too many clients", E_USER_ERROR);
exit;
}
}
if ($i > $maxi)
$maxi = $i;
if (--$nready <= 0)
continue;
}
// check the clients for incoming data.
for ($i = 0; $i <= $maxi; $i++)
{
if ($client[$i] < 0)
continue;
if (socket_fd_isset($allset, $client[$i]))
{
$n = trim(socket_read($client[$i], MAXLINE));
if (!$n)
closeClient($i);
else
{
// if a client has sent some data, do one of these:
if ($n == "/killme")
killDaemon();
else if ($n == "/quit")
closeClient($i);
else
{
// print something on the server, then echo the incoming
// data to all of the clients in the $client array.
print "From {$remote_host[$i]}:{$remote_port[$i]}, client[$i]: $n\n";
for ($j = 0; $j <= $maxi; $j++)
{
if ($client[$j] != -1)
socket_write($client[$j], "From client[$i]: $n\r\n");
}
}
}
if (--$nready <= 0)
break;
}
}
}
?>
Thanks.
Richard.