UPD В первой редакции статьи я ошибочно писал про stream_select. Статья отредактирована подобающим образом.
Обнаружил очень забавное поведение у функции socket_select - она не возвращают информацию об изменениях, если в буфере чтения уже лежат какие-то данные. Получается просто зацикливание приложения на socket_select. Такого поведения в принципе легко достичь, если медленно обрабатывать большой пакет данных.
Алгоритм до безобразия прост.
Шаг 1. socket_select рапортует, что данные в буфере. Вычитываешь их и обрабатываешь.
Шаг 2. Вычитываешь и обрабатываешь предпоследний пакет данных. И пока ты его обрабатываешь в буфер приходит недостающий последний пакет.
Шаг 3. Ты вызываешь socket_select и ждёшь данные. Так как данные уже пришли, socket_select тебе об этом не говорит. Получается, что в буфере уже лежат нужные тебе данные, но не предоставляется никакого механизма узнать об этом. Потому ты будешь ждать ответа пока данные снова не придут в этот сокет. А они могут вообще не придти, если протокол последовательный.
С неблокирующими сокетами - та же проблема. Прежде чем передать сокеты в socket_select надо проверить все буферы чтения. А пока ты их проверяешь, данные могут придти и улечься в буфер чтения. Снова ты их потеряешь.
Выводы:
socket_select в общем случае не подходит для задачи написания сетевого приложения. В общем случае надо использовать работу с неблокирующими сокетами и перебор сокетов. Обнаружил, что недавно зарелизилась libevent для PHP надо будет попробовать инструмент в действии. В конце концов она работает не на селектах, а на epoll и kqueue.
UPD Для написания сетевого приложения можно использовать stream_select, она лишена указанного недостатка socket_select
PS. Спасибо Clanth
Подписаться на:
Комментарии к сообщению (Atom)
5 комментариев:
Попробовал смоделировать такую ситуацию,
код сервра:
$sock = stream_socket_server('tcp://127.0.0.1:11102');
$newsock = stream_socket_accept($sock);
stream_set_blocking($newsock,0);
sleep(10);
$Read = array($newsock);
$Write = $Except = null;
echo 'select...';
$res = stream_select($Read,$Write,$Except,null);
$r = fread($newsock,1024);
echo $r;
sleep(10);
echo 'select...';
$res = stream_select($Read,$Write,$Except,null);
$r = fread($newsock,1024);
echo $r;
fclose($newsock);
fclose($sock);
клинета:
$sock = stream_socket_client('tcp://127.0.0.1:11102');
stream_set_write_buffer($sock,0);
echo 'send 1';
stream_socket_sendto($sock,"test");
sleep(15);
echo 'send 2';
stream_socket_sendto($sock,"test");
sleep(100);
fclose($sock);
Тоесть идет такая последовательность:
1. подключение
2. отправка вообщения №1
3. вызов select
4. считывание сообщения
5. отправка вообщения №2
6. вызов select
все отлично отработало
Мдя, со stream_select я не проверял, а понадеялся на фразу из мана "Its operation is equivalent to that of the socket_select() function except in that it acts on streams."
Проверял я с sock_select. А вот комментария подтверждающий мои слова про sock_select http://ru2.php.net/manual/en/function.socket-select.php#98222
Статью исправлю
Пару месяцев назад выбирал чем пользоваться и натыкался на какие-то проблемы с socket_* функциями, и хотя предпочитаю работать с более низким уровнем, пришлось использовать stream. А где-то читал даже что в socket то ли не исправляют ошибки, то ли сами разработчики рекомендуют не использовать и удивлялись почему не поставят раз так флаг deprecated
а libevent пока нестабильностью (хоть и бета) себя не показала, впрочем по скорости (по крайней мере до 1000 активных соединений) разница неощутима
добрый день. в данные момент пишу серевное приложение на сокетах и столкнулся с тем, что при 20 коннектах и относительно небольшом количестве данных, сокет начинает медленно читать данные от клиентов, приложение "зависает" и в логгере меееедленно идут овтеты клиентам. сбрасываю до 10 клиентов - все окей. пробовал через socket_select, решения Net_SERVER, stream_socket_server - ни в какую. не сталкивались с такой проблемой?
2Alexxz
была та же проблема, select говорил мол "нечего там читать"... попробовал сделать сокет не блокирующим( socket_set_nonblock ) до вызова select... и что Вы думаете? ;) - select сказал что данные есть))
хотелось бы узнать теперь почему это так...
Отправить комментарий