sockets, защита от переполнения.

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-сокета:
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;
}
?>
P.S. не надо удалять сообщение по причине того что кода много. Кому надо тот прочтет а кому не надо тому не надо. Всю ночь тестил :)
С этой проблемой я столкнулся т.к. пишу icq-клиент, меня нервировало то что при потере соеденения такой бред происходил. Разработчикам я уже написал решение проблемы надеюсь послушают.
 

Screjet

Новичок
function socket_wait($sec=NULL,$usec=NULL)
{
if (!socket)...
Есть мнение, что ты это придумывал, пока постил в форум.

Я порылся в исходниках, там переполнение.
Почему бы не указать файл, номер/диапазон строк где именно переполнение?

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

tristram

Guest
придумывал т.к. у меня в виде класса это и там $this->socket. хехехе.. а посмотреть сложно чтоли?
 

tony2001

TeaM PHPClub
>Речь пойдет о недочете в sockets-extension в PHP, исправлений на данный момент нет
есть также мнение, что у тебя хорошая фантазия.

>Разработчикам уже направил тележку.
не видел.
опять фантазия играет?
 

tristram

Guest
tony2001
какая еще фантазия? хочешь проверь сам.
 

tony2001

TeaM PHPClub
проверить что? эту кучу непонятно чего с "собаками" на каждой строке?
когда у тебя будет код до 20-ти строк, без собак и т.п. - приходи.
либо разбирайся в этом коде сам.
 

tristram

Guest
приконнектись к хосту, и жди данные socket_select'ом как написано в мане, а потом отруби коннект со стороны сервера. советую предварительно открыть диспечер задач а то он будет полчаса открываться.
собачки только там где ненужные ошибки высвечиваются
 
Сверху