Сокет демон, чтение с таймаутом

it-master

Новичок
Сокет демон, чтение с таймаутом

Я написал демон, который обрабатывает запросы клиентов в цыкле по одному, по типу HTTP сервера, но появилась проблема при чтении данных socket_read. Если клиент подключился и ничего не передает, то сервер просто ждет его, тем самым не давая шансов следующим клиентам быть обработаными.

Пробовал использовать stream_set_timeout($fp,2) вместе с stream_set_blocking($fp,"FALSE") но таймаута не происходит.
Смотрел в сторону socket_select, но не разобрался в его преимуществах.

Вопрос: как можно установить таймаут на чтение из сокета клиента?

-~{}~ 01.09.06 07:37:

Логика скрипта:

Настраиваем сокет

while(true) {
socket_accept
socket_read
обрабатываем запрос
socket_shutdown
socket_close
}
 

voodoo

Новичок
[m]socket_set_nonblock[/m] попробуй


socket_select скажет тебе какие сокеты готовы к чтению (ну или в случае "один клиент за раз" просто проверит, можно ли что-то читать)
 

AndreyKl

Новичок
таймаут устанавливается когда открываешь сокет. Но, поскольку у всех твоих сокетов таймаут одинаковый, то если один клиент "затормозит" естесственно, по истечении его таймаута истекают таймауты и у других клиентов. Нужно использовать socket_select для того, чтобы выбрать готовый к чтению сокет. Если клиент не отвечает, этот сокет не будет выбран.

-~{}~ 01.09.06 13:02:

А как на php демона сделать? max_execution_time не мешает?

-~{}~ 01.09.06 13:04:

stream_select

-~{}~ 01.09.06 13:10:

Делай как в описании: там (в описании)
PHP:
Логика скрипта:

#Настраиваем сокет

#code nastroiki

#затем открываем и складываем в массив

$sockets[] = fsocketopen(...);

#теперь выбираем из массива готовый сокет и читаем:

if (false === ($num_changed_streams = stream_select($sockets, $write = NULL, $except = NULL, 0))) {
    /* Error handling */
} elseif ($num_changed_streams > 0) {
    /* At least on one of the streams something interesting happened */
}
тока нужен цикл там где if...
 

FreeSpace

Чукча-читатель
Автор оригинала: AndreyKl
А как на php демона сделать? max_execution_time не мешает?
Теоретически это можно сделать в CLI, на практике так никто не делает, ибо изврат.
PHP предназначен для быстрой генерации страниц. Больше секунды - уже очень медленно.
А уж демонов на PHP писать - вообще дело неблагодарное :)
 

AndreyKl

Новичок
мда, в кли действительно работает... и макс_екзек_тайм не мешает нисколько...
 

it-master

Новичок
voodoo
При использовании socket_set_nonblock программа полностью проскакивает socket_read и продолжает выполнение.

-~{}~ 01.09.06 15:44:

AndreyKl
а что произойде в случае c stream_select если настанет таймаут?
 

it-master

Новичок
А как на php демона сделать? max_execution_time не мешает?
Очень даже легко set_time_limit(0), pcntl_fork(), и выполнение в CLI.

-~{}~ 01.09.06 15:51:

voodoo

PHP:
socket_set_nonblock($sock);// Always return right away

$sec = 0;
$j = 0;
while(!$data && $sec <= 2) {
		
if($j>0)
sleep(1);
		
$data = socket_read ($sock, 1024);
		
$j++;
$sec++;
}
и эта конструкция не помогает :(

-~{}~ 01.09.06 15:56:

FreeSpace

просто изучать синтаксис другого языка, если можно тоже самое сделать на PHP с не большими потерями, я считаю не приемлимым.

P.S. а что за язык наиболее "благодарный" под всяких там демонов? :)
 

romy4

invoke [brain]
Автор оригинала: FreeSpace
Теоретически это можно сделать в CLI, на практике так никто не делает, ибо изврат.
PHP предназначен для быстрой генерации страниц.
На PHP написать демона просто и удобно (особенно в условиях выньХР), но вот эта проблемка с несколькими ждущими конектами действительно мешает.
 

AndreyKl

Новичок
AndreyKl
а что произойде в случае c stream_select если настанет таймаут? [/QUOTE]
Где таймаут настанет? В каком месте выполнения?
Не важно... всё будет хорошо, если грамотно написать. Этот способ правельный, только написать надо по-человечески.
 

it-master

Новичок
Так, с socket_select есть прогресс. При истечении таймаута сервер действительно разрывает коннект с клиентом. Но почему-то происходит какая-то странная задержа на socket_accept, когда клиент пытается присоединится и из-за этого срабатывает таймаут и любой клиент не может передать данные :(
 

FreeSpace

Чукча-читатель
Автор оригинала: it-master
FreeSpace

просто изучать синтаксис другого языка, если можно тоже самое сделать на PHP с не большими потерями, я считаю не приемлимым.

P.S. а что за язык наиболее "благодарный" под всяких там демонов? :)
Ну зависит от того, чем именно демон заниматься будет.
А писать можно на чем-то менее веб-ориентированном, начиная от Джавы и C# (только не надо вспоминать про ASP.NET :)), заканчивая классическими Сями.
 

it-master

Новичок
FreeSpace
Я бы написал на С++, но боюсь запутаюсь в самом языке, так как не моя стихия :)

P.S. Хотя обязательно попробую переписать под С++, как полностью закончу написание этого демона на PHP.

-~{}~ 01.09.06 16:32:

Написал вот так:

PHP:
while(true) {

if (($new_sock = socket_accept($main_sock)) < 0) die ("socket_accept() failed: reason: " . socket_strerror ($new_sock));
socket_set_nonblock($new_sock);

$sockets = array();
$sockets[] = $new_sock;

$num_changed_streams = stream_select($sockets, $write = NULL,$except = NULL,2);

if ($num_changed_streams > 0) {
$data = socket_read ($new_sock, 1024);
}

echo $data;

socket_sutdown($new_sock);
socket_close($new_sock);
}
в результате при соединении с клиентом сокет ждет 2 секунды ответа, хотя ответ посылается клиентом сразу после соединения(проверено) и затем прокручивает цикл дальше(за socket_read). :(
 

AndreyKl

Новичок
нашёл здесь
PHP:
#!/usr/local/bin/php
<?php
   $port = 9050;
   $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
   socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
   socket_bind($sock, 0, $port);
   socket_listen($sock);

   $clients = array($sock);
  
   while (true) {
       $read = $clients;       // create a copy, so $clients doesn't get modified by socket_select()
       if (socket_select($read, $write = NULL, $except = NULL, 0) < 1)
           continue;
       if (in_array($sock, $read)) {
           $clients[] = $newsock = socket_accept($sock);
           "There are ".(count($clients) - 1)." client(s) connected to the server\n");
           $key = array_search($sock, $read);
           unset($read[$key]);
       }

       foreach ($read as $read_sock) {
           $data = @socket_read($read_sock, 1024, PHP_NORMAL_READ);
           if ($data === false) {
               $key = array_search($read_sock, $clients);
               unset($clients[$key]);
               echo "client disconnected.\n";
               continue;
           }
           $data = trim($data);
           if (!empty($data)) {
               foreach ($clients as $send_sock) {
                   if ($send_sock == $sock || $send_sock == $read_sock)
                       continue;
#                   socket_write($send_sock, $data."\n"); не уверен, что нужно.. по моему не нужно.
               } // end of broadcast foreach
           }
       } // end of reading foreach
   }
   socket_close($sock);
?>
Отпишись о результатах.. если не получится, будем разбираться подробней.
 

it-master

Новичок
AndreyKl
Большое спасибо, все работает, применил эту конструкцию, немного подогнал под особенности моего демона =))

Спасибо всем, кто помог.
 
Сверху