Не работает скрипт с неблокирующим сокетом

Fakeman

Новичок
Не работает скрипт с неблокирующим сокетом

Есть вот такой кусок код для работы с неблокирующими сокетами, который по какой-от причине не работает... :( Кто-нибудь может подсказать, где моя ошибка? Я грешу на socket_write, потому как в хелпе написано, что записано может быть и меньше байт, чем указано в самой команде в зависимости от некоторых факторов... но что конкретно я делаю не так, не могу понять...

PHP:
// метод connect (приведен в сокращении)
$sock = socket_create()
socket_set_option( $sock,...)
socket_connect( $sock,..);
$this->sockets[$id]["resource"] = $sock;
$this->sockets[$id]["connected"] = false;

// метод run
foreach ( $this->sockets as $sockets ) {
  $read_sockets[] = $sockets["resource"];
  $write_sockets[] = $sockets["resource"];
}

$num_changed_sockets = socket_select ( $read_sockets, $write_sockets, $except = null, 0 );
if ( $num_changed_sockets === false ) {
  $this->_error ( null, "socket_select() failed !" );
  $this->_logger->write ( "ошибка select_socket " . $this->_print_error() );
  break;
} elseif ( $num_changed_sockets > 0 ) {
  $this->_logger->write ( $num_changed_sockets . " changed state" );
  $this->_logger->write ( "sockets for read = " . count ( $read_sockets ) );
  $this->_logger->write ( "sockets for write = " . count ( $write_sockets ) );

// пишем в сокет для записи
foreach ( $write_sockets as $wsid => $ws ) {
  if ( !$this->sockets[$wsid]["connected"] ) {
    $errno = @socket_get_option ( $ws, SOL_SOCKET, SO_ERROR );
    if ( $errno != 0 ) {
      $this->sockets[$wsid]["connected"] = "timeout";
      $this->_logger->write ( "socket " . $wsid . " is timeouted " . $this->_print_error() );
      $this->_disconnect ( $wsid );
    }
  }
  // пишем, если еще не писали
  if ( !$this->sockets[$wsid]["written"] ) {
    $write_data = $this->sockets[$wsid]["request"];
    if ( ( $written_bytes = @socket_write ( $ws, $write_data, strlen ( $write_data ) ) ) === false ) {
      $this->_logger->write ( "socket_write() failed " . $this->_print_error() );
      $this->_disconnect ( $wsid );
    } elseif ( $written_bytes == 0 ) { // по-идее все отослано
      $this->sockets[$wsid]["written"] = true;
      $this->_logger->write ( "socket " . $wsid . " is written" );
    } else {
      $this->_logger->write ( "socket " . $wsid . " is written " . $written_bytes . " bytes" );
    }

  }

}

// читаем из сокета
foreach ( $read_sockets as $rsid => $rs ) {
  if ( ( $read_string = socket_read ( $rs, $this->rcvbuf ) ) === false ) {
    $this->_logger->write ( "socket_read() failed " . $this->_print_error() );
    $this->_disconnect ( $rsid );
  } elseif ( strlen ( $read_string ) == 0 ) {
    $this->results[$this->sockets[$rsid]["url"]] = $this->sockets[$rsid]["response"];
    $this->_logger->write ( "socket " . $rsid . " is stopped talking " . $this->_print_error() );
    $this->_disconnect ( $rsid );
    $this->_logger->write ( "remain sockets " . count ( $this->sockets ) );
  } else {
    $this->sockets[$rsid]["response"] .= $read_string;
    $this->_logger->write ( "socket " . $rsid . " reading - " . $this->sockets[$rsid]["response"] );
  }

}

}
Протокол работы скрипта:

[09:36:36] socket 0 created
[09:36:36] socket set non-blocking
[09:36:36] socket option set successfully
[09:36:36] host is resolved to 127.0.0.1
[09:36:36] socket is connecting... (10035) Операция на незаблокированном сокете не может быть завершена немедленно.
[09:36:36] headers

GET / HTTP/1.0
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: www2.jrc.local
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Referer: http://www2.jrc.local
Connection: Close

[09:36:36] socket selection started
[09:36:36] 1 changed state
[09:36:36] sockets for read = 0
[09:36:36] sockets for write = 1
[09:36:36] socket 0 is written 282 bytes
[09:36:36] 1 changed state
[09:36:36] sockets for read = 0
[09:36:36] sockets for write = 1
[09:36:36] socket 0 is written 282 bytes

... и так ОЧЕНЬ долго... получается, что я никак не могу получить written_bytes = 0, что скажет мне об окончании отсылки...
 

StUV

Rotaredom
Fakeman
можешь упростить свой код до элементарного примера - без лога, обрабочика ошибок, классов и т.п... ???
запусти его, напиши чего от этого скрипта хотел и чего получил.
вряд ли кто-нить будет ковыряться в твоем коде...
 

Fakeman

Новичок
Автор оригинала: StUV
Fakeman
можешь упростить свой код до элементарного примера - без лога, обрабочика ошибок, классов и т.п... ???
запусти его, напиши чего от этого скрипта хотел и чего получил.
вряд ли кто-нить будет ковыряться в твоем коде...
StUV, ты гений! :) ... Упростил код... заодно увидел как минимум одну ошибку... здесь
PHP:
foreach ( $read_sockets as $rsid => $rs ) { 
..
foreach ( $write_sockets as $wsid => $ws ) {
Неверно вычислял id сокета в массиве (нужно было искать через array_search)... и переписал один кусок немного по другому..
Вообщем спасибо тебе.
 

StUV

Rotaredom
Fakeman
// скромно так...
не, я не гений
просто это основы дебага ;)
 

Fakeman

Новичок
А подскажите такую вещь...
Имеем неблокирующий сокет и процесс чтения в/записи из него... socket_write, socket_read... В хелпе написано, что в зависимости от состояния сетевых беферов в сокет или из него может писаться/читаться не все данные буфера, который был указан в той или иной функции, а какая-то часть, чуть ли не 1 байт Но, если для функции socket_read все очевидно и понятно:
PHP:
$acc_buf = "";
$read_str = socket_read ( $sock, 8192 );
if ( strlen ( $read_str  ) == 0 ) {
  echo "сокет прочитан полностью";
} else {
  $acc_buf .= $read_str;
}
т.е. считываем по 8Кб из буфера до тех пор пока не получим признак окончания передачи, т.е. пустую строку, то для функции socket_write не столь очевидно:
PHP:
$written_bytes = socket_write( $sock, $write_buf, strlen ( $write_buf ) );
if ( $written_bytes == 0 ) {
  echo "в сокет записано все"; // кстати, в своих тестах скрипт никогда не попадал в эту ветку... почему?!
} else {
  // а что здесь?
}
Как необходимо писать в сокет? По принципу один раз записали (тогда нам необходимо создать флаг, который разрешит запись в сокет всего один раз) и ждем теперь, когда сокет начнет передавать результат, надеясь, что данные, записанные в сокет, дошли до адресата? Или же пишем в сокет в цикле? Но как тогда сокет определяет, что какие-то данные в него уже записаны? и считывать нужно другую порцию..

PHP:
$num_changed_sockets = @socket_select ( $read_sockets, $write_sockets, $except = null, 0 );
foreach ( $read_sockets as $rs ) {
  // здесь читаем из сокета
}
foreach ( $write_sockets as $ws ) {
  // 1. здесь пишем с сокет
}
А если, например, сервер слишком загружен и скрипт будет ожидать ответа слишком долго, то существует ли возможность прервать соединение по таймауту... ну точнее, возможность есть :) я хочу сказать, можно ли отслеживать таймаут через функцию типа socket_set_timeout? или же нужно делать руками.. напоминаю, что сокеты неблокирующие!
 
Сверху