Операция на незаблокированном сокете не может быть завершена немедленно
Репутация: нет
Всего: 2
На запросе сокет вываливает с ошибкой
Код |
Операция на незаблокированном сокете не может быть завершена немедленно |
Код |
Resource temporarily unavailable. This error is returned from operations on nonblocking sockets that cannot be completed immediately, for example recv when no data is queued to be read from the socket. It is a nonfatal error, and the operation should be retried later. It is normal for WSAEWOULDBLOCK to be reported as the result from calling connect on a nonblocking SOCK_STREAM socket, since some time must elapse for the connection to be established. |
Серверу приходит блок данных. Но перед ним идет заголовок. Он всегда одной длинны. В заголовке
передается как правило длинна блока.
Тоесть сначала сервер вычитывает заголовок. После определеяет длинну данных и вычитывает данные.
Так вот если клиент отсылает все сразу, то тогда работает.
А если он отсылает тоже кусками (тоесть сначала заголовок, а потом и блок), то сервер заголовок получает,
а для того, чтоб получить данные ему приходится ждать (простейший Sleep(100) помог).
З.Ы. И еще. событие FD_READ говорит о том, что серверу например пришел запрос.
Тоесть поймав это событие можно обменивать данными.
Когда сервер пытается отослать данные, то приходит FD_WRITE. Для чего это событие вообще нужно
если через него можно определить лишь что сервер что-то пульнул в сокет
Репутация: 1
Всего: 6
Репутация: нет
Всего: 2
Репутация: 1
Всего: 6
Создать объект события с помощью функции WSACreateEvent.
Выбрать сокет с помощью функции WSAEventSelect.
Ожидать событие с помощью функции WSAWaitForMultipleEvents.
Давайте подробно рассмотрим все функции, необходимые для работы с объектами событий.
Первым делом следует создать событие с помощью функции WSACreateEvent. Функции не надо передавать никаких параметров, она просто возвращает новое событие типа WSAEVENT:
Теперь нужно связать сокет с этим объектом и указать события, которые нам нужны. Для этого используется функция WSAEventSelect:
Первый параметр — это сокет, события которого нас интересуют. Второй параметр — объект события. Последний параметр — это необходимые события. В качестве последнего параметра можно указывать те же константы, что рассматривались для функции WSAAsyncSelect (все они начинаются с префикса FD_).
Раньше вы уже встречались с функциями WaitForSingleObject и WaitForMultipleObjects, которые ожидают наступления события типа HANDLE. Для сетевых событий используется похожая функция с именем WSAWaitForMultipleEvents:
Давайте рассмотрим каждый параметр:
cEvents — количество объектов событий, изменение состояния которых нужно ожидать. Чтобы узнать максимальное число, воспользуйтесь константой WSA_MAXIMUM_WAIT_EVENTS;
lphEvents — массив объектов событий, которые нужно ожидать;
fWaitAll — режим ожидания событий. Если указано TRUE, то функция ожидает, пока все события не сработают, иначе — после первого передает управление программе;
dwTimeOUT — временной интервал в миллисекундах, в течение которого нужно ожидать события. Если в этом временном интервале не возникло события, то функция возвращает значение WSA_WAIT_TIMEOUT. Если нужно ожидать бесконечно, то можно указать константу WSA_INFINITE;
fAlertable — параметр используется при перекрестном вводе/выводе, который я не рассматриваю в этой книге, поэтому указан FALSE.
Чтобы узнать, какое событие из массива событий сработало, нужно вычесть из возвращенного функцией WSAWaitForMultipleEvents значения константу WSA_WAIT_EVENT_0.
Прежде чем вызывать функцию WSAWaitForMultipieEvents, все события в массиве должны быть пустыми. Если хотя бы одно из них будет занято, то функция сразу вернет управление программе, и не будет ожидания. После выполнения функции отработавшие события становятся занятыми, и после обработки их надо освободить. Для этого используется функция WSAResetEvent:
Функция очищает состояние события, указанного в качестве единственного параметра.
Когда событие уже не нужно, его необходимо закрыть. Для этого используется функция WSACloseEvent. Функции следует передать объект события, который необходимо закрыть:
Если закрытие прошло успешно, то функция возвращает TRUE, иначе — FALSE.
Добавлено @ 15:30
А если у тебя с сокетом работает 2 потока и надо узнать когда другой что-то запишет туда?
Репутация: нет
Всего: 2
Dude03, спасибо за подробный ответ.
Но как все-таки ожидать. Может проще слушающий сокет сделать неблокирующим, а клиентскиу после WSAAccept блокирующим
и для него запускать отдельный поток. А после того как поток отработал - убивать сокет.
Но у меня почему-то после
клиент тоже оказывается неблокирующим. Почему ?
Репутация: 1
Всего: 6
Репутация: нет
Всего: 2
не - окна не пойдут - у меня сервис
а как это вообще может быть. Я кручу цикл и ловлю события. Как потоку сказать что для него (для сокета которым он владеет) пришли еще данные ?
Добавлено @ 23:10
Я ведь могу только определить для какого сокета пришло событие
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.
[ Время генерации скрипта: 0.1168 ] [ Использовано запросов: 21 ] [ GZIP включён ]
Здравствуйте, Taurus, Вы писали:
T>сокет клиентский неблокирующий
Можно поинтересоваться, зачем Вам понадобился неблокирующий сокет, если (сюдя по развернувшейся дискуссии, ничего личного) Вы совершенно не знаете, как с ним работать? Не лучше ли будет использовать блокирующий сокет?
Доброго времени суток.
такая вылазит такая "операция на незаблокированном сокете не может быть завершена немедленно", но это же не ошибка? так? как этого избежать?
сокет клиентский неблокирующий
Здравствуйте, Taurus, Вы писали:
T>Доброго времени суток.
T>такая вылазит такая "операция на незаблокированном сокете не может быть завершена немедленно", но это же не ошибка? так? как этого избежать?
T>сокет клиентский неблокирующий
Наверное это WOULDBLOCK. И избегать этого скорее всего не нужно.
Расскажи как ты используешь этот сокет, лучше с примером кода.
Операционка Windows?
Василий Черневич (aka Willi)
Вы писали 11 марта 2004 г., 13:15:01:
T> Доброго времени суток.
T> такая вылазит такая "операция на незаблокированном сокете не
T> может быть завершена немедленно", но это же не ошибка? так? как
T> этого избежать?
T> сокет клиентский неблокирующий
полистайте здесь, можете поиском по WSAEWOULDBLOCK
всё популярно расписано, этот вопро наверно можно назвать вопросом
этого года..
--
С уважением, butcher
Здравствуйте, Willi, Вы писали:
T>>такая вылазит такая "операция на незаблокированном сокете не может быть завершена немедленно", но это же не ошибка? так? как этого избежать?
T>>сокет клиентский неблокирующий
W>Наверное это WOULDBLOCK. И избегать этого скорее всего не нужно.
W>Расскажи как ты используешь этот сокет, лучше с примером кода.
W>Операционка Windows?
Так оно и есть. Я убрал обработку этой ошибки, и прога просто продолжается дальше при ее возникновенни. Так и надо? И еще вопрос, то что localhost сильно тормозит это нормально?
Вы писали 12 марта 2004 г., 9:38:07:
T> Здравствуйте, Willi, Вы писали:
W>>Наверное это WOULDBLOCK. И избегать этого скорее всего не нужно.
W>>Расскажи как ты используешь этот сокет, лучше с примером кода.
W>>Операционка Windows?
T> Так оно и есть. Я убрал обработку этой ошибки, и прога просто
T> продолжается дальше при ее возникновенни. Так и надо? И еще вопрос,
нельзя убирать обработку ошибки, иначе можешь иметь проблемы.
ошибка возникает при чтени или записи?
T> то что localhost сильно тормозит это нормально?
что ты подразумеваешь под "тормозит"?
--
С уважением, butcher
Здравствуйте, butcher, Вы писали:
B>Здравствуйте, Taurus.
B>Вы писали 12 марта 2004 г., 9:38:07:
T>> Здравствуйте, Willi, Вы писали:
W>>>Наверное это WOULDBLOCK. И избегать этого скорее всего не нужно.
W>>>Расскажи как ты используешь этот сокет, лучше с примером кода.
W>>>Операционка Windows?
T>> Так оно и есть. Я убрал обработку этой ошибки, и прога просто
T>> продолжается дальше при ее возникновенни. Так и надо? И еще вопрос,
B> нельзя убирать обработку ошибки, иначе можешь иметь проблемы.
B> ошибка возникает при чтени или записи?
при чтении.
T>> то что localhost сильно тормозит это нормально?
B> что ты подразумеваешь под "тормозит"?
Данные по localhost идут не мгновенно, секунд 30
B>--
B>С уважением, butcher
Вы писали 12 марта 2004 г., 9:56:48:
T> Здравствуйте, butcher, Вы писали:
B>>Здравствуйте, Taurus.
B>>Вы писали 12 марта 2004 г., 9:38:07:
T> Данные по localhost идут не мгновенно, секунд 30
примерно тоже самое можно сказать и про FD_READ, в плане его
обработки.
--
С уважением, butcher
B>примерно тоже самое можно сказать и про FD_READ, в плане его
B>обработки.
какая модель ввода-вывода у вас? select? мало с ней работал..
Если вы хотите что-то прочитать, то вы должны вызвать select для
определения есть ли данные, либо для ожидания, когда данные появятся.
Только после этого читать их.
--
С уважением, butcher
Здравствуйте, butcher, Вы писали:
B>какая модель ввода-вывода у вас? select? мало с ней работал..
B>Если вы хотите что-то прочитать, то вы должны вызвать select для
B>определения есть ли данные, либо для ожидания, когда данные появятся.
B>Только после этого читать их.
Что-то я стал понимать еще меньше, внимательно посмотрел документацию на тему, понял следуюее, прежде чем вызывать функции отправки или получения данных, я должен быть уверен что сокеты готовы получить или отпрвить данные, для этого используется select. Тогда абсолютно не понятно разделение на блокируюие и неблокирующие сокеты, и в том и в другом случае я должен вызвать по идее select а потом ужу заниматся отправкой/полчучением данных, причем (если я не вызываю select) в случае отсутствия данных, в случае блокирующих сокетов я буду поток будет приостонавливатся и ждать появления данных, в случае неблокирующих данных будет возвращена ошибка, так? Тогда я что-то не понимаю.
С уважением Taurus
Здравствуйте, Michael Chelnokov, Вы писали:
MC>Здравствуйте, Taurus, Вы писали:
T>>сокет клиентский неблокирующий
MC>Можно поинтересоваться, зачем Вам понадобился неблокирующий сокет, если (сюдя по развернувшейся дискуссии, ничего личного) Вы совершенно не знаете, как с ним работать? Не лучше ли будет использовать блокирующий сокет?
В данном случае можно обойтись конечно и блокирующими сокетами, но тогда мне придется заводить рабочий поток, и заниматся синхронизацией главного и рабочего потоков. А блокирование главного потока никуда не годится, клиент не знает и не может знать когда и скоько придет.
Так я думал 15 мин. назад, а щас хз, с учетом того что мне в любом случае нелбходимо использовать select
С уважением Taurus
Вы писали 13 марта 2004 г., 23:32:23:
T> .
T> данных, в случае неблокирующих данных будет возвращена ошибка, так?
T> Тогда я что-то не понимаю.
под виндой есть более удобные способы асинхронной работы с
вводом-выводом (см. WSAEventSelect, WSAAsyncSelect, WSAOVERLAPPED).
По теме:
Есть два вида сокетов — блокирующие и неблокирующие:
1. При некоторых действиях с сокетом, действие может заблокировать
рабочий поток до полного завершения действия либо до возникновения
ошибки.
2. Неблокирующие, при выполнении тех же действий функция не блокирует
поток. Функции возвращают управление немедленно, не зависимо от того,
успели они его завершить или нет. Если успели — то они работают так же
как и блокирующие, выполняют действие и возвращают удачный код
возврата. Если не успели — возвращают код ошибки WSAEWOULDBLOCK —
который значит, что функция не может быть завершена немедленно и
требует блокировки (а она выклбючена).
В случае с селект, ты должен ждать, когда система будет готова
продолжить операцию.
Таким образом, алгоритм работы получается такой:
* посылка данных — ты посылаешь данные, если получаешь ошибку от
send'a, то либо вызываешь select с ожиданием момента готовности к
посылке, либо что-то делаешь вместо посылки (и после этого вызываешь
select с ожиданием готовности посылки данных).
* чтение данных — читаешь, если получаешь ошибку, либо ждёшь с помощью
select'а, либо делаешь что-то ещё и после ждёшь готовности к посылке.
под ошибкой я подразумеваю WSAEWOULDBLOCK, при других ошибках — нужно
их обрабатывать по другому.
--
С уважением, butcher
Вы писали 13 марта 2004 г., 23:37:46:
T> Так я думал 15 мин. назад, а щас хз, с учетом того что мне в
T> любом случае нелбходимо использовать select
расскажи пожалуйста, что могло вызвать такую необходимость? В виндовс
я вижу только одну — возможность более безболезненного портирования
приложения например в UNIX. Если этого не нужно — я советую
попробовать другую модель ввода-вывода.
--
С уважением, butcher
Здравствуйте, butcher, Вы писали:
B>* чтение данных — читаешь, если получаешь ошибку, либо ждёшь с помощью
B>select'а, либо делаешь что-то ещё и после ждёшь готовности к посылке.
А если я сразу же вызову select? до получения ошибки? и буду ждать появления даннвх?
Вы писали 14 марта 2004 г., 22:56:14:
T> А если я сразу же вызову select? до получения ошибки? и буду ждать появления даннвх?
ты всё равно можешь получить WSAEWOULDBLOCK, даже если данные есть.
Отслеживать ошибку надо.
--
С уважением, butcher
Здравствуйте, butcher, Вы писали:
T>> А если я сразу же вызову select? до получения ошибки? и буду ждать появления даннвх?
B>ты всё равно можешь получить WSAEWOULDBLOCK, даже если данные есть.
Это как?
Я понял отчего тормоза были во времени дохождения пакета Окно с серваком было неактивно и поэтому ничего не делало
Написал я себе класс для работы с неблокирующими сокетами. Сокеты создаются, коннектятся, но результат получить из них я не могу. Прошу вас глянуть кусок кода, может вы подскажете, где я что-то сделал не так:
// Метод connect
$socket = @socket_create ( AF_INET, SOCK_STREAM, SOL_TCP );
if ( !is_resource ( $socket ) ) $this->_error ( $socket, "socket_create() failed !", true );
>
$this->_logger->write ( "socket " . $id . " created" );
if ( !@socket_set_nonblock ( $socket ) ) $this->_logger->write ( "socket_set_nonblock() failed " . $this->_print_error() );
$this->_disconnect ( $id );
>
$this->_logger->write ( "socket set non-blocking" );
if ( !@socket_set_option ( $socket, SOL_SOCKET, SO_REUSEADDR, true ) ) $this->_logger->write ( "socket_set_option() failed " . $this->_print_error() );
$this->_disconnect ( $id );
>
$this->_logger->write ( "socket option set successfully" );
if ( ( $connect_ip = @gethostbyname ( $host ) ) == $host ) $this->_logger->write ( "gethostbyname() failed " . $this->_print_error() );
$this->_disconnect ( $id );
>
$this->_logger->write ( "host is resolved to " . $connect_ip );
$connected = @socket_connect ( $socket, $host, $port );
if ( ( $this->_errno() != EWOULDBLOCK ) && ( $this->_errno() != EINPROGRESS ) ) $this->_logger->write ( "socket_connect() failed " . $this->_print_error() );
$this->_disconnect ( $id );
>
$this->_logger->write ( "socket is connecting. " . $this->_print_error() );
// fill socket array
$this->sockets[$id]["resource"] = $socket;
$this->sockets[$id]["url"] = $url;
$this->sockets[$id]["connected"] = false;
$this->sockets[$id]["written"] = false;
$this->sockets[$id]["request"] = $this->httprequest ( $id );
$this->sockets[$id]["response"] = false;
// Метод run
$this->_method = "run";
$this->_logger->write ( "socket selection started" );
$this->results = array();
while ( count ( $this->sockets ) > 0 ) // initialize arrays for socket select
$read_sockets = $write_sockets = array();
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->_logger->write ( "couldn't 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 sockets for write 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 );
>
>
// write only if the socket was not written
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" );
$this->_logger->write ( "remain to send\n" . $this->sockets[$wsid]["request"] );
>
>
>
// read data from sockets
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
[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
.
Итак мегабайты лога. правда где-то в конце я получаю, что 2 сокета изменили состояние. и там еще тот бред.
Я грешу на socket_write. но что и как не пойму. бьюсь уже несколько дней.
<?
//получить содержимое
$urls = array ( $ip1 , $ip2 ) ;
$rtasks = array ( ) ; // задачи чтения
$wtasks = array ( ) ; // задачи записи
$results = array ( ) ; // результаты
foreach ( $urls as $url ) <
// открываем отдельный сокет
$sh = socket_create ( AF_INET , SOCK_STREAM , SOL_TCP ) ;
if ( ! $sh ) continue ;
// таймаут для чтения
socket_set_option ( $sh , SOL_SOCKET , SO_RCVTIMEO , array ( "sec" => 10 , "usec" => 0 ) ) ;
// таймаут для записи
socket_set_option ( $sh , SOL_SOCKET , SO_SNDTIMEO , array ( "sec" => 10 , "usec" => 0 ) ) ;
// задаем неблокирующий режим сокетов
socket_set_nonblock ( $sh ) ;
// соединяемся через telnet-порт
socket_connect ( $sh , $url , 23 ) ;
// добавляем в задачи для записи
$wtasks [ $url ] = $sh ;
>
// продолжаем, пока есть задачи для записи или чтения
while ( $wtasks || $rtasks ) <
// массив для сокетов с возможностью чтения
$rtasks_ = $rtasks ;
// массив для сокетов с возможностью записи
$wtasks_ = $wtasks ;
// ждем результатов из сокетов
$n = socket_select ( $rtasks_ , $wtasks_ , $e = null , 10 ) ;
if ( $n > 0 ) <
// сокеты, доступные для записи
foreach ( $wtasks_ as $sh ) <
// ищем урл страницы по дескриптору сокета в массиве задач записи
$url = array_search ( $sh , $wtasks ) ;
// удаляем из задач записи
unset ( $wtasks [ $url ] ) ;
// добавляем в задачи чтения
$rtasks [ $url ] = $sh ;
// формируем задание
$per = $login . " \r \n " ;
$per .= $password . " \r \n " ;
$per .= "enable \r \n " ;
$per .= "display vlan all \r \n " ;
$per .= " \r \n " ;
// записываем в сокет
if ( socket_write ( $sh , $per ) === false ) fclose ( $sh ) ;
>
// сокеты, доступные для чтения
foreach ( $rtasks_ as $sh ) <
// ищем урл страницы по дескриптору сокета в массиве задач чтения
$url = array_search ( $sh , $rtasks ) ;
if ( ! $url ) continue ;
// считываем результат из сокета
$result = '' ;
while ( $r = socket_read ( $sh , 1024 ) ) $result .= $r ;
// закрываем сокет
socket_close ( $sh ) ;
// удаляем из задач чтения
unset ( $rtasks [ $url ] ) ;
// заносим html в массив результатов
$results [ $url ] = $result ;
>
>
else <
break ;
>
>
Читайте также: