(перевод с англ., источник: PHP Socket server and Chat Gateway for Flash clients)
Первоначально, я не планировал писать пошаговое руководство "PHP сокет сервер и чат шлюз для флеш клиентов за 10 минут", или что-то похожее.
Я закончил разработку руководства флеш-чата, использующего PHP в качестве бэкэнда и шлюза. Я опубликовал свой код и некоторые решения в качестве примера как флеш-приложение общается с сервером по 80 порту.
Я не включил флеш-часть в свой документ, т.к. ее делал мой коллега и друг. Я не могу опубликовать этот код.
Для общих сведений, как мы это делали, Вы можете почитать руководство kirupa.com - PHP 5 Sockets with Flash 8.
Таким образом, в этой статье я покажу вам наши решения чат-сервера на PHP 5, флеш-клиентах, эмулятора веб-сервера, использующего кросс-доменную политику из файла crossdomain.xml, отдаваемому по запросу, и общение с сервером, базирующемуся на xml-сообщениях. Этот пример только показывает, как создать многопользовательский чат с приватным общением. Т.е. вы можете разговаривать сразу с несколькими людьми, но не в чат-комнатах (каналах).
Будем использовать PHP из командной строки, т.к. в полноценном веб-сервере нет необходимости.
Для начала, нам необходимо создать демон (фоновый процесс в Windows), без временного лимита на исполнения. Скрипт в данном случае выполняется до конца света, или до первой перезагрузки :). Также мы установим IP адрес и порт для прослушивания.
#!/usr/bin/php -q set_time_limit(0); ob_implicit_flush(); $address = '127.0.0.1'; $port = 80; Давайте мы созданим массив для входящих подключений (если вы используете его для чата, в нем можно будет хранить ники). Далее создаем сокет. Все echo сообщения в нашем примере идут в лог-файл. $_sockets = array(); if (($master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) { echo "socket_create() failed, reason: " . socket_strerror($master) . "\n"; } socket_set_option($master, SOL_SOCKET,SO_REUSEADDR, 1); if (($ret = socket_bind($master, $address, $port)) < 0) { echo "socket_bind() failed, reason: " . socket_strerror($ret) . "\n"; } if (($ret = socket_listen($master, 5)) < 0) { echo "socket_listen() failed, reason: " . socket_strerror($ret) . "\n"; } else { $started=time(); echo "[".date('Y-m-d H:i:s')."] SERVER CREATED ( MAXCONN:".SOMAXCONN." ) \n"; echo "[".date('Y-m-d H:i:s')."] Listening on ".$address.":".$port."\n"; } $read_sockets = array($master);
SOMAXCONN является переменной ядра. Она устанавливает количество подключений, которые может обработать ваш сервер. В Unix она может быть установлена на уровне ядра и скорректирована командой sysctl.
После этого мы создаем бесконечный цикл для обработки запросов
while (true) {
$changed_sockets = $read_sockets;
$num_changed_sockets = socket_select($changed_sockets, $write = NULL, $except = NULL, NULL);
foreach($changed_sockets as $socket) {
if ($socket == $master) {
if (($client = socket_accept($master)) < 0) {
echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
continue;
} else {
array_push($read_sockets, $client);
echo "[".date('Y-m-d H:i:s')."] CONNECTED "."(".count($read_sockets)."/".SOMAXCONN.")\n";
}
}
else
{
$bytes = @socket_recv($socket, $buffer, 2048, 0);
/*
Here comes the core... ;)
*/
}
}
Это основа. До данного момента, этот код такой же, как в публикации Raymond Fain в socketShell.php, которая упоминалась выше.
Как я уже говорил, мы будем править еще часть кода.
Я буду цитировать каждый кусок кода в следующей части. Мы начнем с определения функций, которые описываются вне бесконечного цикла.
Сначала мы сделаем эмулятор веб-сервера. Зачем нам это нужно?
Суть системы состоит в том, чтобы общаться по 80-му порту, т.к. файрволлы не будут блокировать сообщения а нем. Но проблема заключается в том что, как вы знаете, флеш по умолчанию общается по 1024 порту. Что неприемлемо из-за политик безопастности.
Есть способ, чтобы "научить" флеш общаться по любому порту, которому вы хотите. Это называется кросс-доменной политикой.
Политики хранятся в XML-формате, который выглядит в нашем случае так:
Я храню ее в переменной. Мой демон, обслуживающий сокеты, получает запрос от флеш.
В ответ мы отдаем файл кроссдоменной политики, что означает, что мы разрешаем подключение по 80 порту.
Если вы хотите подробно почитать о кроссдоменных политиках, зайдите на http://www.crossdomainxml.org/
Ваш код после socket_recv будет выглядеть так:
if (preg_match("/policy-file-request/i", $buffer) || preg_match("/crossdomain/i", $buffer)) {
echo "[".date('Y-m-d H:i:s')."] CROSSDOMAIN.XML REQUEST\n";
$contents=' ';
socket_write($socket,$contents);
$contents="";
$index = array_search($socket, $read_sockets);
unset($read_sockets[$index]);
socket_shutdown($socket, 2);
socket_close($socket);
}
Мы закрываем сокет, поскольку это флеш-запрос на файл кроссдоменной политики. Получив данный файл, флеш-приложение закрывает соединение и переподключается на "правильном" сокете.
После обработчика для флеш-клиентов, создадим подробные сообщения о состоянии сервера (состоянии демона и /server-status). Они могут быть просмотрены, если набрать в браузере IP адрес (и порт, если он отличный от 80). Набрав http://127.0.0.1/server-status, мы будем получать такую информацию:
OK
Clients: 48/128
Created: 2007-07-10 10:54:02
Uptime: 16 days
Далее. Я игнорирую запросы favicon.ico, потому что если будет сделан запрос из браузера, он автоматически запросит данную информацию и это может вызывать какие-нибудь ошибки кода.
И последнее. Я эмулирую веб-серверную часть, чтобы перенаправить все GET и POST запросы с HTTP заголовками, которые идут не через сокет.
Мы также сделаем запись в журнале запросов и создадим переадресацию HTTP.
Итак, давайте посмотри, что мы должны добавить в существующий код, чтобы он заработал описанным выше образом:
elseif (( preg_match("/GET/", $buffer) || preg_match("/POST/", $buffer)) && preg_match("/HTTP/", $buffer))
{
if (preg_match("//server-status/i", $buffer))
{
$uptime = floor((time()-$started)/86400);
socket_write($socket,"OK\n");
socket_write($socket,"Clients: ".count($read_sockets)."/".SOMAXCONN."\n");
socket_write($socket,"Created: ".date('Y-m-d H:i:s',$started)."\n");
socket_write($socket,"Uptime: ".$uptime." days\n");
echo "[".date('Y-m-d H:i:s')."] STATUS REQUEST\n";
}
elseif (preg_match("/favicon.ico/i", $buffer))
{
//ignore :)
}
else
{
// fake web server
socket_write($socket,"HTTP/1.1 301 Moved Permanently\n");
socket_write($socket,"Server: PHP Chat Server by DjZoNe - http://djz.hu/\n");
socket_write($socket,"Date: ".date("d, j M Y G:i:s T\n"));
socket_write($socket,"Last-Modified: ".date("d, j M Y G:i:s T\n"));
socket_write($socket,"Location: http://djz.hu/\n");
}
$index = array_search($socket, $read_sockets);
unset($read_sockets[$index]);
@socket_shutdown($socket, 2);
@socket_close($socket);
}
Пока что мы сделали часть, стартующую сокет-сервер и перенаправляющую HTTP-запросы. В следующей части мы напишем обработчик ситуации, когда сокет закрывается и пользователь отключается.
if (strlen($buffer) == 0) {
// мы берем уникальный ID пользователя из базы данных
$id=$_sockets[intval($socket)]['nick'];
$index = array_search($socket, $read_sockets);
unset($read_sockets[$index]); // we clean up
unset($_sockets[intval($socket)]); // we clean up our own data
// cleaning up is essential when creating a daemon
// we can't leave junk in the memory
@socket_shutdown($socket, 2);
@socket_close($socket);
$allclients = $read_sockets; // перезагружаем активных клиентов
// $socket is now pointing to a dead resource id
// but the send_Message() function will need it, I'll explain later
send_Message($allclients, "");
echo "[".date('Y-m-d H:i:s')."] QUIT ".$id."\n";
}
И вот сейчас реальная сокет-коммуникация:
else {
$allclients = $read_sockets;
array_shift($allclients);
$piece = explode(" ",trim($buffer)); // we strip out all unwanted data
$cmd = strtoupper($piece[0]);
}
Мы используем несколько команд IRC-протокола.
MSG сообщение
IDENTIFY ник пароль
LIST
Мы разбиваем сообщение на куски, и склеиваем его после того, как только определим первые несколько аргументов.
if (!empty($piece[1])) $content = $piece[1];
switch ($cmd) {
case "IDENTIFY":
$id = trim($piece[1]);
$passwd = trim($piece[2]);
send_Identify($allclients, $socket, $id, $passwd);
break;
case "MSG":
$id = trim($piece[1]);
$msg="";
foreach ($piece as $key=>$val)
{
if ($key > "1") $msg.=$val." ";
}
$msg = trim($msg);
send_Msg($allclients, $socket, $id, $msg);
break;
case "LIST":
list_Users($allclients, $socket);
break;
}
Мы сделали вызовы команд.
До сих пор мы создавали цикл.
Сейчас мы будем создавать функции. Снаружи.
Я хочу рассказать вам историю.... Просто шутка ;)
Мы сделали socket_write и все. Мы не сможем получить через сокет информацию, пока не поместим символ ASCII 0 в конец
буфера. Т.е. мы вводим ноль ASCII после каждого socket_write. Думаю, для XML-сокет коммуникации через Flash это просто.
Если мы посмотрим назад в "эмулятор веб-сервера", мы просто добавим одну строчку.
Вот вам несколько функций, которые мы используем для авторизации, отправки сообщений и т.д.
function send_Identify($allclients, $socket, $id, $passwd)
{
global $_sockets;
$nicks = array();
$dbconf = new DATABASE_CONFIG;
$db_host = $dbconf->host;
$db_base = $dbconf->database;
$db_login = $dbconf->login;
$db_password = $dbconf->password;
foreach ($_sockets as $_socket)
{
foreach ($_socket as $key=>$val)
{
if (empty($nicks[$val])) $nicks[$val]=1;
else $nicks[$val]=$nicks[$val]+1;
}
}
if (empty($nicks[$id]))
{
$s=1;
// Here will be a simple authentication.
$link = mysql_connect($db_host, $db_login, $db_password);
if (!$link) die("Could not connect:" . mysql_error() . "\n");
$db_selected = mysql_select_db($db_base, $link);
if (!$db_selected) die("Can't use $db_base :" . mysql_error() . "\n");
$result = mysql_query("SELECT nick FROM members WHERE id='".intval($id)."' AND password='".crypt($passwd)."' AND
active='1' LIMIT 1");
$data = mysql_fetch_array($result);
$name = $data['name'];
$_sockets[intval($socket)]=array('id'=>$id, 'nick'=>$name);
mysql_free_result($result);
mysql_close($link);
После использования SQL-соединения его необходимо закрыть. Это важно, т.к. соединение будет закрываться по тайм-ауту и
демон умрет.
}
else $s=0;
// We'll answer to the flash in XML form.
// But we receive in plain text format.
if ($s == 1)
{
$out = "";
send_Message($allclients, "");
// this goes to all active, identified clients
echo "[".date('Y-m-d H:i:s')."] LOGIN ".$id."(".count($allclients)."/".SOMAXCONN.")\n";
}
else $out = "";
socket_write($socket, $out.chr(0)); // write back to the client
}
function send_Msg($allclients,$socket,$id,$msg)
{
global $_sockets;
if (!empty($_sockets[intval($socket)]))
{
$nicks = array(); //amig fut a parancs ebben vannak a nickek.
foreach ($_sockets as $_socket)
{
foreach ($_socket as $key=>$val)
{
// this check's the onliners
if (empty($nicks[$val])) $nicks[$val]=1;
else $nicks[$val]=$nicks[$val]+1; // we shouldn't have duplicated nicks, but what if...
}
}
foreach($allclients as $client)
{
if (!empty($_sockets[$client]['nick']) && ($_sockets[$client]['nick'] == $id))
{
$_client = $client;
$out = "
from=\"".$_sockets[$client]['nick']."\" />";
}
elseif(empty($nicks[$id]))
//not online or something similar
{
//backto the sender
$_client = $socket;
$out = "";
}
}
}
else
{
//backto the sender
$_client = $socket;
$out = "";
}
if (!empty($out))
{
socket_write($socket, $out.chr(0)); //send to back ourself. we have to handle it in flash
socket_write($_client, $out.chr(0)); //send to the recipient
}
}
Сейчас мы создали функцию, которая посылает сообщение всем присоединенным клиентам. Далее покажем, как получить список всех пользователей.
function send_Message($allclients, $socket, $buf) {
global $_sockets;
foreach($allclients as $client) {
@socket_write($client, $buf.chr(0));
}
}
function list_Users($allclients,$socket) {
global $_sockets;
$out = "";
foreach($allclients as $client) {
if (!empty($_sockets[$client]['nick']) && ($_sockets[$client]['nick'] != "")) {
$out .= "";
}
}
$out .= " ";
socket_write($socket, $out.chr(0));
}
?>
В данный момент наш демон обрабатывает три основные команды - идентификацию, получение списка пользователей и отправку
сообщений. Это то, что я обещал вам в самом начале. Вы можете доработать код. Например, мы не имеем функции для смена ника (команды /nick в IRC) и т.д.
Это все. Исходник может быть загружен с здесь.
А в конце у меня для Вас подарок. Это маленький BASH скрипт для запуска демона:
#!/bin/sh
if [ "X$1" = "Xstart" ] ; then
chmod +x /var/www/chat/phpircgateway.php
/var/www/chat/phpircgateway.php >> /var/log/chat/chat.log &
echo "Starting chat"
fi
Первоначально, я не планировал писать пошаговое руководство "PHP сокет сервер и чат шлюз для флеш клиентов за 10 минут", или что-то похожее.
Я закончил разработку руководства флеш-чата, использующего PHP в качестве бэкэнда и шлюза. Я опубликовал свой код и некоторые решения в качестве примера как флеш-приложение общается с сервером по 80 порту.
Я не включил флеш-часть в свой документ, т.к. ее делал мой коллега и друг. Я не могу опубликовать этот код.
Для общих сведений, как мы это делали, Вы можете почитать руководство kirupa.com - PHP 5 Sockets with Flash 8.
Таким образом, в этой статье я покажу вам наши решения чат-сервера на PHP 5, флеш-клиентах, эмулятора веб-сервера, использующего кросс-доменную политику из файла crossdomain.xml, отдаваемому по запросу, и общение с сервером, базирующемуся на xml-сообщениях. Этот пример только показывает, как создать многопользовательский чат с приватным общением. Т.е. вы можете разговаривать сразу с несколькими людьми, но не в чат-комнатах (каналах).
Будем использовать PHP из командной строки, т.к. в полноценном веб-сервере нет необходимости.
Для начала, нам необходимо создать демон (фоновый процесс в Windows), без временного лимита на исполнения. Скрипт в данном случае выполняется до конца света, или до первой перезагрузки :). Также мы установим IP адрес и порт для прослушивания.
#!/usr/bin/php -q set_time_limit(0); ob_implicit_flush(); $address = '127.0.0.1'; $port = 80; Давайте мы созданим массив для входящих подключений (если вы используете его для чата, в нем можно будет хранить ники). Далее создаем сокет. Все echo сообщения в нашем примере идут в лог-файл. $_sockets = array(); if (($master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) { echo "socket_create() failed, reason: " . socket_strerror($master) . "\n"; } socket_set_option($master, SOL_SOCKET,SO_REUSEADDR, 1); if (($ret = socket_bind($master, $address, $port)) < 0) { echo "socket_bind() failed, reason: " . socket_strerror($ret) . "\n"; } if (($ret = socket_listen($master, 5)) < 0) { echo "socket_listen() failed, reason: " . socket_strerror($ret) . "\n"; } else { $started=time(); echo "[".date('Y-m-d H:i:s')."] SERVER CREATED ( MAXCONN:".SOMAXCONN." ) \n"; echo "[".date('Y-m-d H:i:s')."] Listening on ".$address.":".$port."\n"; } $read_sockets = array($master);
SOMAXCONN является переменной ядра. Она устанавливает количество подключений, которые может обработать ваш сервер. В Unix она может быть установлена на уровне ядра и скорректирована командой sysctl.
После этого мы создаем бесконечный цикл для обработки запросов
while (true) {
$changed_sockets = $read_sockets;
$num_changed_sockets = socket_select($changed_sockets, $write = NULL, $except = NULL, NULL);
foreach($changed_sockets as $socket) {
if ($socket == $master) {
if (($client = socket_accept($master)) < 0) {
echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
continue;
} else {
array_push($read_sockets, $client);
echo "[".date('Y-m-d H:i:s')."] CONNECTED "."(".count($read_sockets)."/".SOMAXCONN.")\n";
}
}
else
{
$bytes = @socket_recv($socket, $buffer, 2048, 0);
/*
Here comes the core... ;)
*/
}
}
Это основа. До данного момента, этот код такой же, как в публикации Raymond Fain в socketShell.php, которая упоминалась выше.
Как я уже говорил, мы будем править еще часть кода.
Я буду цитировать каждый кусок кода в следующей части. Мы начнем с определения функций, которые описываются вне бесконечного цикла.
Сначала мы сделаем эмулятор веб-сервера. Зачем нам это нужно?
Суть системы состоит в том, чтобы общаться по 80-му порту, т.к. файрволлы не будут блокировать сообщения а нем. Но проблема заключается в том что, как вы знаете, флеш по умолчанию общается по 1024 порту. Что неприемлемо из-за политик безопастности.
Есть способ, чтобы "научить" флеш общаться по любому порту, которому вы хотите. Это называется кросс-доменной политикой.
Политики хранятся в XML-формате, который выглядит в нашем случае так:
Я храню ее в переменной. Мой демон, обслуживающий сокеты, получает запрос от флеш.
В ответ мы отдаем файл кроссдоменной политики, что означает, что мы разрешаем подключение по 80 порту.
Если вы хотите подробно почитать о кроссдоменных политиках, зайдите на http://www.crossdomainxml.org/
Ваш код после socket_recv будет выглядеть так:
if (preg_match("/policy-file-request/i", $buffer) || preg_match("/crossdomain/i", $buffer)) {
echo "[".date('Y-m-d H:i:s')."] CROSSDOMAIN.XML REQUEST\n";
$contents='
socket_write($socket,$contents);
$contents="";
$index = array_search($socket, $read_sockets);
unset($read_sockets[$index]);
socket_shutdown($socket, 2);
socket_close($socket);
}
Мы закрываем сокет, поскольку это флеш-запрос на файл кроссдоменной политики. Получив данный файл, флеш-приложение закрывает соединение и переподключается на "правильном" сокете.
После обработчика для флеш-клиентов, создадим подробные сообщения о состоянии сервера (состоянии демона и /server-status). Они могут быть просмотрены, если набрать в браузере IP адрес (и порт, если он отличный от 80). Набрав http://127.0.0.1/server-status, мы будем получать такую информацию:
OK
Clients: 48/128
Created: 2007-07-10 10:54:02
Uptime: 16 days
Далее. Я игнорирую запросы favicon.ico, потому что если будет сделан запрос из браузера, он автоматически запросит данную информацию и это может вызывать какие-нибудь ошибки кода.
И последнее. Я эмулирую веб-серверную часть, чтобы перенаправить все GET и POST запросы с HTTP заголовками, которые идут не через сокет.
Мы также сделаем запись в журнале запросов и создадим переадресацию HTTP.
Итак, давайте посмотри, что мы должны добавить в существующий код, чтобы он заработал описанным выше образом:
elseif (( preg_match("/GET/", $buffer) || preg_match("/POST/", $buffer)) && preg_match("/HTTP/", $buffer))
{
if (preg_match("//server-status/i", $buffer))
{
$uptime = floor((time()-$started)/86400);
socket_write($socket,"OK\n");
socket_write($socket,"Clients: ".count($read_sockets)."/".SOMAXCONN."\n");
socket_write($socket,"Created: ".date('Y-m-d H:i:s',$started)."\n");
socket_write($socket,"Uptime: ".$uptime." days\n");
echo "[".date('Y-m-d H:i:s')."] STATUS REQUEST\n";
}
elseif (preg_match("/favicon.ico/i", $buffer))
{
//ignore :)
}
else
{
// fake web server
socket_write($socket,"HTTP/1.1 301 Moved Permanently\n");
socket_write($socket,"Server: PHP Chat Server by DjZoNe - http://djz.hu/\n");
socket_write($socket,"Date: ".date("d, j M Y G:i:s T\n"));
socket_write($socket,"Last-Modified: ".date("d, j M Y G:i:s T\n"));
socket_write($socket,"Location: http://djz.hu/\n");
}
$index = array_search($socket, $read_sockets);
unset($read_sockets[$index]);
@socket_shutdown($socket, 2);
@socket_close($socket);
}
Пока что мы сделали часть, стартующую сокет-сервер и перенаправляющую HTTP-запросы. В следующей части мы напишем обработчик ситуации, когда сокет закрывается и пользователь отключается.
if (strlen($buffer) == 0) {
// мы берем уникальный ID пользователя из базы данных
$id=$_sockets[intval($socket)]['nick'];
$index = array_search($socket, $read_sockets);
unset($read_sockets[$index]); // we clean up
unset($_sockets[intval($socket)]); // we clean up our own data
// cleaning up is essential when creating a daemon
// we can't leave junk in the memory
@socket_shutdown($socket, 2);
@socket_close($socket);
$allclients = $read_sockets; // перезагружаем активных клиентов
// $socket is now pointing to a dead resource id
// but the send_Message() function will need it, I'll explain later
send_Message($allclients, "
echo "[".date('Y-m-d H:i:s')."] QUIT ".$id."\n";
}
И вот сейчас реальная сокет-коммуникация:
else {
$allclients = $read_sockets;
array_shift($allclients);
$piece = explode(" ",trim($buffer)); // we strip out all unwanted data
$cmd = strtoupper($piece[0]);
}
Мы используем несколько команд IRC-протокола.
MSG сообщение
IDENTIFY ник пароль
LIST
Мы разбиваем сообщение на куски, и склеиваем его после того, как только определим первые несколько аргументов.
if (!empty($piece[1])) $content = $piece[1];
switch ($cmd) {
case "IDENTIFY":
$id = trim($piece[1]);
$passwd = trim($piece[2]);
send_Identify($allclients, $socket, $id, $passwd);
break;
case "MSG":
$id = trim($piece[1]);
$msg="";
foreach ($piece as $key=>$val)
{
if ($key > "1") $msg.=$val." ";
}
$msg = trim($msg);
send_Msg($allclients, $socket, $id, $msg);
break;
case "LIST":
list_Users($allclients, $socket);
break;
}
Мы сделали вызовы команд.
До сих пор мы создавали цикл.
Сейчас мы будем создавать функции. Снаружи.
Я хочу рассказать вам историю.... Просто шутка ;)
Мы сделали socket_write и все. Мы не сможем получить через сокет информацию, пока не поместим символ ASCII 0 в конец
буфера. Т.е. мы вводим ноль ASCII после каждого socket_write. Думаю, для XML-сокет коммуникации через Flash это просто.
Если мы посмотрим назад в "эмулятор веб-сервера", мы просто добавим одну строчку.
Вот вам несколько функций, которые мы используем для авторизации, отправки сообщений и т.д.
function send_Identify($allclients, $socket, $id, $passwd)
{
global $_sockets;
$nicks = array();
$dbconf = new DATABASE_CONFIG;
$db_host = $dbconf->host;
$db_base = $dbconf->database;
$db_login = $dbconf->login;
$db_password = $dbconf->password;
foreach ($_sockets as $_socket)
{
foreach ($_socket as $key=>$val)
{
if (empty($nicks[$val])) $nicks[$val]=1;
else $nicks[$val]=$nicks[$val]+1;
}
}
if (empty($nicks[$id]))
{
$s=1;
// Here will be a simple authentication.
$link = mysql_connect($db_host, $db_login, $db_password);
if (!$link) die("Could not connect:" . mysql_error() . "\n");
$db_selected = mysql_select_db($db_base, $link);
if (!$db_selected) die("Can't use $db_base :" . mysql_error() . "\n");
$result = mysql_query("SELECT nick FROM members WHERE id='".intval($id)."' AND password='".crypt($passwd)."' AND
active='1' LIMIT 1");
$data = mysql_fetch_array($result);
$name = $data['name'];
$_sockets[intval($socket)]=array('id'=>$id, 'nick'=>$name);
mysql_free_result($result);
mysql_close($link);
После использования SQL-соединения его необходимо закрыть. Это важно, т.к. соединение будет закрываться по тайм-ауту и
демон умрет.
}
else $s=0;
// We'll answer to the flash in XML form.
// But we receive in plain text format.
if ($s == 1)
{
$out = "
send_Message($allclients, "
// this goes to all active, identified clients
echo "[".date('Y-m-d H:i:s')."] LOGIN ".$id."(".count($allclients)."/".SOMAXCONN.")\n";
}
else $out = "";
socket_write($socket, $out.chr(0)); // write back to the client
}
function send_Msg($allclients,$socket,$id,$msg)
{
global $_sockets;
if (!empty($_sockets[intval($socket)]))
{
$nicks = array(); //amig fut a parancs ebben vannak a nickek.
foreach ($_sockets as $_socket)
{
foreach ($_socket as $key=>$val)
{
// this check's the onliners
if (empty($nicks[$val])) $nicks[$val]=1;
else $nicks[$val]=$nicks[$val]+1; // we shouldn't have duplicated nicks, but what if...
}
}
foreach($allclients as $client)
{
if (!empty($_sockets[$client]['nick']) && ($_sockets[$client]['nick'] == $id))
{
$_client = $client;
$out = "
from=\"".$_sockets[$client]['nick']."\" />";
}
elseif(empty($nicks[$id]))
//not online or something similar
{
//backto the sender
$_client = $socket;
$out = "
}
}
}
else
{
//backto the sender
$_client = $socket;
$out = "
}
if (!empty($out))
{
socket_write($socket, $out.chr(0)); //send to back ourself. we have to handle it in flash
socket_write($_client, $out.chr(0)); //send to the recipient
}
}
Сейчас мы создали функцию, которая посылает сообщение всем присоединенным клиентам. Далее покажем, как получить список всех пользователей.
function send_Message($allclients, $socket, $buf) {
global $_sockets;
foreach($allclients as $client) {
@socket_write($client, $buf.chr(0));
}
}
function list_Users($allclients,$socket) {
global $_sockets;
$out = "
foreach($allclients as $client) {
if (!empty($_sockets[$client]['nick']) && ($_sockets[$client]['nick'] != "")) {
$out .= "
}
}
$out .= "
socket_write($socket, $out.chr(0));
}
?>
В данный момент наш демон обрабатывает три основные команды - идентификацию, получение списка пользователей и отправку
сообщений. Это то, что я обещал вам в самом начале. Вы можете доработать код. Например, мы не имеем функции для смена ника (команды /nick в IRC) и т.д.
Это все. Исходник может быть загружен с здесь.
А в конце у меня для Вас подарок. Это маленький BASH скрипт для запуска демона:
#!/bin/sh
if [ "X$1" = "Xstart" ] ; then
chmod +x /var/www/chat/phpircgateway.php
/var/www/chat/phpircgateway.php >> /var/log/chat/chat.log &
echo "Starting chat"
fi
1 комментарий:
I don't understand your language, but I'm glad you translated my article, into your language :)
Thank you for your effort.
Отправить комментарий