tristram
Guest
sockets, защита от переполнения.
Речь пойдет о недочете в [m]sockets[/m]-extension в PHP, исправлений на данный момент нет. Суть такова что если открывается сокет через socket_create, а далее соеденяемся с удаленным хостом, ставим опцию nonblock, т.е. процедура read не ждет данных (останавливая работу скрипта), обмениваемся данными то при отключение происходит ошибка. Т.е. если удаленный хост отключается (это запланировано и обсолютно адекватно), то PHP начинает жрать простите за выражение ресурсы, через 5 секунд уже 99% cpu. Память кушается медленее но всё равно. Я порылся в исходниках, там переполнение. Разработчикам уже направил тележку. А пока нам, простым смертным остается лишь выкинуть [m]socket_read[/m] на помойку и взять на вооружение [m]socket_recv[/m]. Эта функция не документирована по-этому я решил состряпать документацию и пример. Теперь могу сказать доброе утро, так как оно наступило
.
(PHP 4 >= 4.1.0, PHP 5)
socket_recv -- Receives data from a connected socket
int socket_recv ( resource socket, string &buf, int len, int flags )
(PHP 4 >= 4.1.0, PHP 5)
socket_recv -- Получает данные из подключенного сокета.
int socket_recv ( resource socket, string &buf, int len, int flags )
resource socket это указатель на сокет, его возвращает результат [m]socket_create[/m] (а не [m]fsockopen[/m])
string &buffer это ссылочная переменная для получение данных
int len это длина требуемых данных
int flags это опции чтения:
0 - смешаный режим (если не работает 2-ой вариант то попробуйте этот)
1 - PHP_NORMAL_READ - чтение данных до \n (новая строка) или \r (символ возврата коретки)
2 - PHP_BINARY_READ - бинарное чтение (не пугайтесь, тоже самое только до конца len)
Заметка: лучше всего использовать Режим 0.
Возможные ответы функции:
int 0 - удаленный хост закрыл соеденение,
int N < 0 - ошибка
Заметка: получить ошибку можно с помощью [m]socket_last_error[/m] - код последней ошибки, [m]socket_strerror[/m] - получение описание ошибки, если вы делаете выводы есть ли ошибка по наличию кода - это дурной тон, но в этом случае используйте [m]socket_clear_error[/m] для очистки.
int length - длина пришедших данных
bool FALSE - данных нет
Заметка: если используется nonblock-опция и данные еще не пришли то вернется FALSE.
Заметка: если пришло int 0 то данных нет
Расширеный пример чтения nonblock-сокета:
P.S. не надо удалять сообщение по причине того что кода много. Кому надо тот прочтет а кому не надо тому не надо. Всю ночь тестил 
С этой проблемой я столкнулся т.к. пишу icq-клиент, меня нервировало то что при потере соеденения такой бред происходил. Разработчикам я уже написал решение проблемы надеюсь послушают.
Речь пойдет о недочете в [m]sockets[/m]-extension в PHP, исправлений на данный момент нет. Суть такова что если открывается сокет через socket_create, а далее соеденяемся с удаленным хостом, ставим опцию nonblock, т.е. процедура read не ждет данных (останавливая работу скрипта), обмениваемся данными то при отключение происходит ошибка. Т.е. если удаленный хост отключается (это запланировано и обсолютно адекватно), то PHP начинает жрать простите за выражение ресурсы, через 5 секунд уже 99% cpu. Память кушается медленее но всё равно. Я порылся в исходниках, там переполнение. Разработчикам уже направил тележку. А пока нам, простым смертным остается лишь выкинуть [m]socket_read[/m] на помойку и взять на вооружение [m]socket_recv[/m]. Эта функция не документирована по-этому я решил состряпать документацию и пример. Теперь могу сказать доброе утро, так как оно наступило

(PHP 4 >= 4.1.0, PHP 5)
socket_recv -- Receives data from a connected socket
int socket_recv ( resource socket, string &buf, int len, int flags )
(PHP 4 >= 4.1.0, PHP 5)
socket_recv -- Получает данные из подключенного сокета.
int socket_recv ( resource socket, string &buf, int len, int flags )
resource socket это указатель на сокет, его возвращает результат [m]socket_create[/m] (а не [m]fsockopen[/m])
string &buffer это ссылочная переменная для получение данных
int len это длина требуемых данных
int flags это опции чтения:
0 - смешаный режим (если не работает 2-ой вариант то попробуйте этот)
1 - PHP_NORMAL_READ - чтение данных до \n (новая строка) или \r (символ возврата коретки)
2 - PHP_BINARY_READ - бинарное чтение (не пугайтесь, тоже самое только до конца len)
Заметка: лучше всего использовать Режим 0.
Возможные ответы функции:
int 0 - удаленный хост закрыл соеденение,
int N < 0 - ошибка
Заметка: получить ошибку можно с помощью [m]socket_last_error[/m] - код последней ошибки, [m]socket_strerror[/m] - получение описание ошибки, если вы делаете выводы есть ли ошибка по наличию кода - это дурной тон, но в этом случае используйте [m]socket_clear_error[/m] для очистки.
int length - длина пришедших данных
bool FALSE - данных нет
Заметка: если используется nonblock-опция и данные еще не пришли то вернется FALSE.
Заметка: если пришло int 0 то данных нет
Расширеный пример чтения nonblock-сокета:
PHP:
<?php
function socket_wait($sec=NULL,$usec=NULL)
{
if (!socket) {_error("socket","socket failed (".socket_last_error()." - ".socket_strerror(socket_last_error()).")",__FILE__,__LINE__); return FALSE;}
else
{
if ($sec === NULL) {list($sec,$usec) = explode(" ",timeout);}
$sec = intval($sec);
$usec = intval($usec);
$read = array($socket);
$ret = @socket_select($read,$write = NULL,$except = NULL,$sec,$usec);
if ($ret === FALSE) {return FALSE;}
elseif ($ret) {return $ret;}
else {return 0;}
}
}
function _error($procedure,$error,$file="unknown",$line="unknown")
{
echo $procedure."() error: ".$error." in file ".$file." at line ".$line.".\n";
return;
}
function read ($len=0,$sec=0,$usec=500000)
{
@socket_clear_error($socket);
$wait = socket_wait($sec,$usec);
$waiterr = @socket_last_error($socket);
$recv = @socket_recv($socket,$data,$len,0);
if ($wait === FALSE) {return FALSE;}
elseif ($wait === 1)
{
if ($recv === FALSE) // Experimental
{
_error("read","connection closed by remote host or communication error (".socket_last_error($socket)." - ".socket_strerror($socket_last_error()).")",__FILE__,__LINE__);
disconnect();
}
}
{
if ($waiterr)
{
_error("read","connection failed (".socket_last_error()." - ".socket_strerror(socket_last_error()).")",__FILE__,__LINE__);
@socket_clear_error(socket);
}
else
{
if ($recv < 0) {_error("read","socket_recv() failed (".socket_last_error()." - ".socket_strerror(socket_last_error()).")",__FILE__,__LINE__);}
@socket_clear_error($socket);
if ($recv === FALSE) {$datalen = 0; $data = NULL;}
elseif (is_numeric($recv)) {$datalen = $recv;}
if ($datalen !== strlen($data)) {_error("read","recieved data length (".$datalen.") !== real data length (".strlen($data).")",__FILE__,__LINE__);}
if ($datalen !== $len and $datalen !== 0) {_error("read","recieved data length (".$datalen.") !== needed data length (".$len.") ",__FILE__,__LINE__);}
return $data;
}
}
}
function disconnect(&$socket)
{
if ($socket) {@socket_close($socket);}
$socket = FALSE;
return FALSE;
}
?>

С этой проблемой я столкнулся т.к. пишу icq-клиент, меня нервировало то что при потере соеденения такой бред происходил. Разработчикам я уже написал решение проблемы надеюсь послушают.