Эту проблему я наблюдал и на С++ и на С# и на java ...
Причем успешно прочитать 0 байт можно как минимум по 2 причинам
1) Есть сокетные соединения с программой Android OC при бездействии экрана на противоположенном конце может прийти несколько пустых пакетов (может баг???)
2) Если одну из программ придушить диспетчером задач, то на противоположенном конце часто периодически сыплются такие пустые приёмы и грузят процессор довольно долго(покудо вдруг Win не сообразит выдать ошибку)
Как в таких случаях быть?
Что можно придумать кроме отсылки NOOP с таймаутом?
Здравствуйте, vsb, Вы писали:
vsb>man read
vsb>On success, the number of bytes read is returned (zero indicates end of file)
vsb>В случае сокета это означает, что соединение закрыто. Непонятно, в чём тут проблема? Прочитал 0, закрыл соединение со своей стороны и всё.
кстати ты прав но почему для закрытых сокетов есть отдельные коды ошибок?
Здравствуйте, vvv848165@ya.ru, Вы писали:
VYR>кстати ты прав но почему для закрытых сокетов есть отдельные коды ошибок?
Про какие именно коды ошибок идёт речь? Возможно ты говоришь о ситуации, когда сокет закрыт ненормальным образом, например connection reset.
Т.е. соединение может быть закрыто нормальным образом, когда та сторона вызывает shutdown, в этом случае приходят одни пакеты (FIN).
Соединение может быть "оборвано", например если на той стороне программа упала и соединение подчищает уже ОС или программа вызвала close без shutdown. В этом случае приходят другие пакеты (RST).
А ещё соединение может просто отвалиться по таймауту, если давно не приходило никаких пакетов с той стороны. Это тоже ошибка.
В общем лучше всего подцепиться wireshark-ом и смотреть на конкретные пакеты и то, как они соотносятся с вызываемыми функциями/возвращаемыми значениями.
Ты прав! но как определить конкретный случай ? чтобы не закрыть случайно не совсем безнадёжное соединение из-за одного нулевого ?
vsb>А ещё соединение может просто отвалиться по таймауту, если давно не приходило никаких пакетов с той стороны.
вроде почти день тестил соединения Win — Win или и Android — Android по таймауту так просто не обрывались
а вот Win — Android или любой *nix — Android жди кучу бед (одно из них пустые пакеты)
Здравствуйте, vvv848165@ya.ru, Вы писали:
VYR>Эту проблему я наблюдал и на С++ и на С# и на java ... VYR>Причем успешно прочитать 0 байт можно как минимум по 2 причинам
Для TCP прочтение 0 байт — надежный признак того, что соединение закрыли с другой стороны. В случае UDP реально можно получить пустой пакет (пакет, содержащий 0 байтов данных). Но зато у UDP не бывает закрытия соединения с другой стороны.
Здравствуйте, netch80, Вы писали:
N>На TCP это означает, что та сторона закрылась — можно дальше читать до посинения, будет только ответ 0. N>На UDP — пустая датаграмма (законная штука).
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, netch80, Вы писали:
N>>На TCP это означает, что та сторона закрылась — можно дальше читать до посинения, будет только ответ 0. N>>На UDP — пустая датаграмма (законная штука).
Pzz>А для SCTP?
Отправка пустого сообщения получает отказ EINVAL — по крайней мере под Linux и BSD.
Поэтому чтение аналогично TCP — пришла длина 0, значит, закрыли с той стороны.
Здравствуйте, Pzz, Вы писали:
Pzz>Ну например потому, что кроме нормального закрытия соединения бывают еще всякие аварийные варианты. И их может возникнуть желание отличать.
а как-нибудь что-нибудь дополнительно можно узнать при получении 0 байт по TCP-IP (ну хоть через какие нибудь Api)?
Здравствуйте, vvv848165@ya.ru, Вы писали:
M>>Не понятно, какой конкретный случай ты собрался определять?
VYR>Дак уже говорили что 0 байт может прийти например из-за Shutdown в одну сторону...
Ну и закрывай себе. Либо отправляй то что хотел напоследок, и опять же закрывай. В чём проблема-то?
Здравствуйте, vvv848165@ya.ru, Вы писали:
VYR>Эту проблему я наблюдал и на С++ и на С# и на java ...
Может вы всё-таки будете внимательно читать документацию по сокетам? Там явно сказано всё то, что вам в теме написали. Прямо вот на MSDN, и ещё в паре десятков книжек и примеров по сетевому программированию.
Здравствуйте, vvv848165@ya.ru, Вы писали:
Pzz>>Ну например потому, что кроме нормального закрытия соединения бывают еще всякие аварийные варианты. И их может возникнуть желание отличать.
VYR>а как-нибудь что-нибудь дополнительно можно узнать при получении 0 байт по TCP-IP (ну хоть через какие нибудь Api)?
В таких аварийных вариантах вы получите не 0, а -1, и уточняющий код в errno.
Здравствуйте, vvv848165@ya.ru, Вы писали:
vsb>> это означает, что соединение закрыто
VYR>Вот то-то и оно что для этого есть специальные коды ошибок и исключения, а не успешный 0 !
Таки непонятен ваш плач. Я правильно понял, что речь, что на той стороне программа не сама решила завершить передачу по соединению в соответствии со своей штатной логикой, а её вместо этого пришлёпнули, и вам хотелось бы видеть в этом случае другой код, чем 0?
Если да — я согласен, что тут есть проблема. Она есть и в варианте локального пайпа (шелл собрал цепочку типа xx | yy, xx отстрелили — yy получит только EOF), и в сетевом. Увы. Но так как сетевое соединение двустороннее, можно спросить состояние — а можно добавить явную метку конца в протокол. Или как на HTTP сделано: посылайте Content-Length или используйте chunked TE, тогда метка завершения всегда будет явной.
Я бы при проектировании этого интерфейса подумал про такие индикации. Но раз за ~30 лет их активного использования не исправляли — проблема считается не настолько серьёзной.
Здравствуйте, netch80, Вы писали:
N>Таки непонятен ваш плач. Я правильно понял, что речь, что на той стороне программа не сама решила завершить передачу по соединению в соответствии со своей штатной логикой, а её вместо этого пришлёпнули, и вам хотелось бы видеть в этом случае другой код, чем 0?
Разве в таком случае будет 0? Вроде должен прийти пакет RST и будет -1 с econnreset.
Здравствуйте, vsb, Вы писали:
N>>Таки непонятен ваш плач. Я правильно понял, что речь, что на той стороне программа не сама решила завершить передачу по соединению в соответствии со своей штатной логикой, а её вместо этого пришлёпнули, и вам хотелось бы видеть в этом случае другой код, чем 0?
vsb>Разве в таком случае будет 0? Вроде должен прийти пакет RST и будет -1 с econnreset.
Проверьте у себя, не вопрос.
Я вижу, что Linux и FreeBSD — возвращают 0.
вы не понимаете современную молодежь
зачем читать и разбираться ?
достаточно прийти на ктыв, закидать пацанов вопросами, пусть разбираются, объясняют, дают правильные советы
Здравствуйте, vvv848165@ya.ru, Вы писали:
VYR>Как в таких случаях быть?
Удивлен, что до сих пор никто не дал правильного ответа.
Чтение TCP сокета может возвращать ноль если:
1) Происходит реакция сетевого стека на фрагментацию пакетов. Фрагментация может возникать с определенными устройствами в определенных конфигурациях сети при определенных нагрузках или сценариях. Обычно, в локальной сети фрагментация возникает редко, и поэтому многие неопытные разработчики даже не догадываются о такой возможности, потому что она редко (или почти) не возникает в тепличных условиях
2) Спонтанное пробуждение примитивов ожидания ОС, вызванное особенностями работы ядра (обычное явление во многих ОС)
Что делать если сокет вернул меньше чем ожидалось?
Продолжать читать до тех пор, пока не наберете нужный обьем информации! Или пока сокет не отвалится с ошибкой.
ПОПРАВКА: как верно подметили коллеги, эти рекомендации слишком общие и напоминают сферического коня в ваккуме.
Поэтому нужно учитывать ОС, на которой работает ваше приложение. Подитожу:
В некоторых встраиваемых системах: чтение сокета может спорадически возвращать ноль в виду перечисленных выше причин, и при этом не означать закрытия сокета или ошибки. Это говорит о неполноценной реализации сетевого стека конкретной ОС
Windows: чтение сокета возвращает ноль только при полном или частичном закрытии сокета. Такой сокет уже считается закрытым на чтение, и все дальнейшие попытки прочитать из него будут всегда моментально возвращать 0. Это не означает ошибку, это означает закрытый сокет.
Unix: чтение сокета возвращает ноль только если система не желает удерживать системный вызов в блокирующем состоянии (EINTR, EGAIN, EWOULDBLOCK) или при полном/частичном закрытии сокета.
Здравствуйте, Aquilaware, Вы писали:
A> 2) Спонтанное пробуждение примитивов ожидания ОС, вызванное особенностями работы ядра (обычное явление во многих ОС)
Здравствуйте, Слава, Вы писали:
С>Может вы всё-таки будете внимательно читать документацию по сокетам? Там явно сказано всё то, что вам в теме написали. Прямо вот на MSDN, и ещё в паре десятков книжек и примеров по сетевому программированию.
Здравствуйте, Aquilaware, Вы писали:
M>>Емнип там ошибка будет
A>Неа. 0 это не ошибка. Это значит — попробуй еще, пока что не набралось. Касается только сокетов.
A>Для файлов на файловой системе 0 возвращается когда достигнут конец файла.
Здравствуйте, Aquilaware, Вы писали:
A>Здравствуйте, vvv848165@ya.ru, Вы писали:
VYR>>Как в таких случаях быть?
A>Удивлен, что до сих пор никто не дал правильного ответа.
Здравствуйте, Marty, Вы писали:
M>Не. Должна вернуть ошибку, а errno будет EINTR
Как-то так, но EINTR это даже не ошибка, а призыв повторить системный вызов, потому что он был прерван по какой-либо причине:
Many system calls will report the EINTR error code if a signal occurred while the system call was in progress. No error actually occurred, it's just reported that way because the system isn't able to resume the system call automatically.
В случае Unix, одной из причин может стать сигнал. Но я видел достаточно встраиваемых сетевых стеков, там где никаких сигналов даже нет, но поведение операции чтения из TCP сокета такое же, естественно без EINTR, а просто 0 в результате. Следующие попытки чтения успешны, сокет не разрывается, просто вот в силу причин иногда проскакивает 0. Windows — то же самое.
Здравствуйте, Aquilaware, Вы писали:
M>>Не. Должна вернуть ошибку, а errno будет EINTR
A>Как-то так, но EINTR это даже не ошибка, а призыв повторить системный вызов, потому что он был прерван по какой-либо причине:
Ну, код ошибки анализировать таки надо, да. А то еще бывают всякие EWOULDBLOCK etc.
Главное, что read не 0 возвращает
A>В случае Unix, одной из причин может стать сигнал. Но я видел достаточно встраиваемых сетевых стеков, там где никаких сигналов даже нет, но поведение операции чтения из TCP сокета такое же, естественно без EINTR, а просто 0 в результате. Следующие попытки чтения успешны, сокет не разрывается, просто вот в силу причин иногда проскакивает 0.
Ну, это кривые реализации, что тут поделать. Надо курить доки/сорцы конкретного стека, и тюнить под него. Но так не должно быть
A>Windows — то же самое.
Здравствуйте, reversecode, Вы писали:
R>Здравствуйте, Aquilaware, Вы писали:
A>>Здравствуйте, vvv848165@ya.ru, Вы писали:
VYR>>>Как в таких случаях быть?
A>>Удивлен, что до сих пор никто не дал правильного ответа.
R>да что вы говорите ? R>http://man7.org/linux/man-pages/man2/recv.2.html R>
R> When a stream socket peer has performed an orderly shutdown, the
R> return value will be 0 (the traditional "end-of-file" return).
R>и сколько мисье написал сетевых программ за свою жизнь?
Несколько десятков.
См. EINTR на который Marty указал. Shutdown это только один из случаев, но он не единственный. Нужно errno смотреть. У каждого стека колокольчики со свистками.
Собственно, автор топика не акцентировал внимание на конкретной ОС. Если есть конкретика, то нужно маны покурить. А если вобщем почему сокет возвращает 0 и errno = EINTR или вообще success, то ответ мисье надеюсь предоставил достаточно исчерпывающей.
Здравствуйте, Marty, Вы писали:
M>Никогда такого не видел под виндой
Да, винду я мог по инерции приплести. Там это скорее всего означает то же что и в Unix минус сигналы: сокет закрыт на чтение и читать из него более нельзя.
Здравствуйте, Aquilaware, Вы писали:
A>Удивлен, что до сих пор никто не дал правильного ответа.
Да, настолько неправильных ответов тут ещё не давали — и это хорошо.
A>Чтение сокета может возвращать ноль если: A> 1) Происходит реакция сетевого стека на фрагментацию пакетов. Фрагментация может возникать с определенными устройствами в определенных конфигурациях сети при определенных нагрузках или сценариях. Обычно, в локальной сети фрагментация возникает редко, и поэтому многие неопытные разработчики даже не догадываются о такой возможности, потому что она редко (или почти) не возникает в тепличных условиях
Если вы не читаете каким-нибудь tcpdump, вы не увидите фрагмент пакета. Вообще.
A> 2) Спонтанное пробуждение примитивов ожидания ОС, вызванное особенностями работы ядра (обычное явление во многих ОС)
Если речь про сигналы, будет -1 и EINTR.
В некоторых особых условиях можно получить -1 и EAGAIN.
В особо некоторых — EWOULDBLOCK, который отличается от EAGAIN (там, где отличается)
Если включено nonblocking, получено -1 и EAGAIN — это штатно.
A>Что делать если сокет вернул 0 или меньше чем ожидалось?
A>Продолжать читать до тех пор, пока не наберете нужный обьем информации! Или пока сокет не отвалится с ошибкой.
Особенно "ценный" совет для SOCK_DGRAM или SOCK_SEQPACKET.
Здравствуйте, netch80, Вы писали:
N>Откуда ж вас такого юмориста принесло-то...
С полей под названием внедрение и реальность.
result=0 и errno=success был замечен при чтении из TCP сокета на некоторых встраиваемых системах, с которыми мне приходилось работать. Стеки там были собраны как говорится из "г..на и палок", иногда даже с "интересными" кусками кода датированными концом 80-x.
Насчет фрагментации. Её вы таки увидите при чтении из TCP сокета даже на "больших" системах! Но проявляется она не как EINTR, а как возвращаемое значение больше 0, но меньше чем просили.
Особенно забавный был случай, когда один из разработчиков написал приемник протокольных пакетов, и ожидал что read для сокета возращает именно столько байт, сколько попросили в аргументе. И если результат был меньше, он то ли выкидывал его, то ли вовсе переоткрывал сокет, думая что передатчик "не окончил полностью передачу".
Вот эта ситуация проявляется редко, но связана она именно с фрагментацией. В "тихой" сети, всё работало. Но, стояло другой сотне аппаратов поднятся, как некоторые начинали терять протокольное соединение с серверои. Это было очень очень неприятно отлаживать.
В итоге смогли повторить ситуацию даже на локальной машине в пределах localhost, просто запустив сотню прогаммных эмуляторов устройств вместе с сервером на одной машине. Зацепились отладчиком и увидели эту каку.
(Все эти детали касаются TCP. С датаграмами по-другому, я о них не говорю вообще)
Здравствуйте, Aquilaware, Вы писали:
A>Здравствуйте, netch80, Вы писали:
N>>Откуда ж вас такого юмориста принесло-то...
A>С полей под названием внедрение и реальность.
A>result=0 и errno=success был замечен при чтении из TCP сокета на некоторых встраиваемых системах, с которыми мне приходилось работать. Стеки там были собраны как говорится из "г..на и палок", иногда даже с "интересными" кусками кода датированными концом 80-x.
Ааа, "некоторые встраиваемые"... ну тогда надо всё-таки с самого начала уточнять, что речь о заведомо кривых реализациях. Думаю, тот, кто возится именно с такой реализацией, вряд ли задал бы тут вопрос, не уточняя обстановку.
Android это всё-таки Linux-based решение, там такого не увидеть.
Хотя ещё вопрос — там случайно SysV флаг O_NDELAY не использовался? С ним можно такое получить. Но ни в BSD ни в Linux его принципиально не переносили.
A>Насчет фрагментации. Её вы таки увидите при чтении из TCP сокета даже на "больших" системах! Но проявляется она не как EINTR, а как возвращаемое значение больше 0, но меньше чем просили.
Спасибо, кэп Хотя, да, напоминать про это надо всегда — каждый второй индус (в общем смысле) будет думать, что если что-то одним send() отправили, то точно так же одним recv() и будет получено. Причём он будет тестировать на раздельных пакетах, и будет говорить "у меня всё работает". Тут спасут только массовые расстрелы.
A>Особенно забавный был случай, когда один из разработчиков написал приемник протокольных пакетов, и ожидал что read для сокета возращает именно столько байт, сколько попросили в аргументе. И если результат был меньше, он то ли выкидывал его, то ли вовсе переоткрывал сокет, думая что передатчик "не окончил полностью передачу".
Во-во.
A>Вот эта ситуация проявляется редко, но связана она именно с фрагментацией. A> В "тихой" сети, всё работало. Но, стояло другой сотне аппаратов поднятся, как некоторые начинали терять протокольное соединение с серверои. Это было очень очень неприятно отлаживать.
В TCP/IP это _не_ называется фрагментацией. Это следствие упаковки нескольких порций отсылки в один IP пакет. Фрагментацией называется, когда IP пакет режется на отправке или по дороге потому, что на каком-то из линков недостаточный MTU.
Может, вы понимаете явление правильно, но термин неуместный.
A>В итоге смогли повторить ситуацию даже на локальной машине в пределах localhost, просто запустив сотню прогаммных эмуляторов устройств вместе с сервером на одной машине. Зацепились отладчиком и увидели эту каку.
Верю, сам видел похожее но давайте не сбивать с толку терминологией.
A>(Все эти детали касаются TCP. С датаграмами по-другому, я о них не говорю вообще)
Здравствуйте, netch80, Вы писали:
N>В TCP/IP это _не_ называется фрагментацией. Это следствие упаковки нескольких порций отсылки в один IP пакет. Фрагментацией называется, когда IP пакет режется на отправке или по дороге потому, что на каком-то из линков недостаточный MTU. N>Может, вы понимаете явление правильно, но термин неуместный.
Здравствуйте, Aquilaware, Вы писали:
A>Несколько десятков.
A>См. EINTR на который Marty указал. Shutdown это только один из случаев, но он не единственный. Нужно errno смотреть. У каждого стека колокольчики со свистками.
Здравствуйте, vvv848165@ya.ru, Вы писали: VYR>Эту проблему я наблюдал и на С++ и на С# и на java ... VYR>Причем успешно прочитать 0 байт можно как минимум по 2 причинам VYR>1) Есть сокетные соединения с программой Android OC при бездействии экрана на противоположенном конце может прийти несколько пустых пакетов (может баг???) VYR>2) Если одну из программ придушить диспетчером задач, то на противоположенном конце часто периодически сыплются такие пустые приёмы и грузят процессор довольно долго(покудо вдруг Win не сообразит выдать ошибку)
Я тут мимо проходил...
Клиент и сервер на одном компьютере (Win10).
У меня никто никого не придушивал.
В нагрузочном тесте на клиенте вылезла такая ошибка:
[winsock][BUG CHECK] Чтение из закрытого порта подключения. Ошибка WinSock: 0.
Код чтения из порта
size_t RemoteFB__INET_Socket::Helper__Socket_Recv(size_t const cb,
void* const pv)
{
assert(cb>0);
assert(pv!=nullptr);
CHECK_WRITE_PTR(pv,cb);
const int icb=structure::can_numeric_cast<int>(cb)
?static_cast<int>(cb)
:structure::t_numeric_limits<int>::max_value();
assert(icb>0);
assert(size_t(icb)<=cb);
const int n=m_spProvider->m_recv.point()
(m_hSocket,
reinterpret_cast<char*>(pv),
icb,
RemoteFB__INET_StaticCfg::c_FB_RECV_FLAGS);
if(n==SOCKET_ERROR)
{
const int winsockErr=m_spProvider->m_WSAGetLastError.point()();
//! \todo
//! Process interupt error?
// ERROR - failed to read from INET port
t_ibp_error exc(E_FAIL,
ibp_mce_winsock__failed_to_read_from_port_1,
IBP_CreateCustomErrorFor_CnFailed());
exc<<winsockErr;
exc.raise_me();
}//if n==SOCKET_ERRORif(n==0) //<----------------------- ВОТ ЗДЕСЬ ПРИЕЗЖАЕТ НОЛЬ И ВЫКИДЫВАЕТСЯ ОШИБКА.
{
const int winsockErr=m_spProvider->m_WSAGetLastError.point()();
// ERROR - [BUG CHECK] read from closed socket?
t_ibp_error exc(E_FAIL,
ibp_mce_winsock__bug_check__read_from_closed_port_1,
IBP_CreateCustomErrorFor_CnFailed());
exc<<winsockErr;
exc.raise_me();
}//ifif(n<0)
{
// ERROR - unexpected result value from recvconst int winsockErr=m_spProvider->m_WSAGetLastError.point()();
t_ibp_error exc(E_FAIL,
ibp_mce_winsock__bug_check__unexpected_result_code_from_recv_operation_2,
IBP_CreateCustomErrorFor_CnFailed());
exc<<n<<winsockErr;
exc.raise_me();
}//if n<0if(n>icb)
{
// ERROR - [BUG CHECK] read incorrect number of bytes
t_ibp_error exc(E_FAIL,
ibp_mce_winsock__bug_check__recv_read_more_than_expected_2,
IBP_CreateCustomErrorFor_CnFailed());
exc<<n<<icb;
exc.raise_me();
}//ifreturn static_cast<size_t>(n);
}//Helper__Socket_Recv
Сам сервер не упал.
Что там произошло —
Такая херня начала происходить относительно недавно (впервые — может месяц, два назад). Сейчас вот второй раз вижу.
Windows последний раз обновлялась в прошлом году.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --