Вы уверены в своем API? (Документация , которую я нашел , удивительно тиха в отношении возвращаемого значения 0
или упорядоченного закрытия сокета.)
Базовые recv()
или read()
вызовы стека TCP возвращают 0
, когда узел закрыл соединение. Из справочной страницы recv(2)
в моей системе Linux:
RETURN VALUE
These calls return the number of bytes received, or -1 if an
error occurred. The return value will be 0 when the peer has
performed an orderly shutdown.
Обратите внимание, что может быть несколько байтов или вплоть до гигабайта данных или более данных, оставленных в сокете для чтения даже после того, как одноранговый узел закрыл соединение. Ваше серверное приложение может не получить уведомление FIN
о закрытом сокете в течение продолжительного времени после завершения работы клиента, в зависимости от задержки и пропускной способности ваших каналов, стратегии ввода-вывода вашего приложения и доступной памяти Стек TCP / IP для буферизации данных сокета.
Следующий следующий recv()
вызов (после того, как он однажды вернулся 0
) вернет ошибку. Я предполагаю, что интерфейс HawkNL к сокетам будет следовать этому поведению.
Кроме того, nlShutdown
выглядит очень отличающимся от обычного значения TCP для shutdown()
, которое просто сообщает стеку TCP, что приложение завершило чтение, запись или чтение и запись на одном конкретная розетка. Использование его в вашем клиенте может быть немного драконовским.
Редактировать
Я немного просмотрел источник HawkNL, и теперь я еще больше запутался в поведении, которое вы видите.
NL_EXP NLint NL_APIENTRY nlRead(NLsocket socket, NLvoid *buffer, NLint nbytes)
{
if(driver)
{
if(nlIsValidSocket(socket) == NL_TRUE)
{
if(buffer == NULL)
{
nlSetError(NL_NULL_POINTER);
}
else
{
NLint received;
if(nlLockSocket(socket, NL_READ) == NL_FALSE)
{
return NL_INVALID;
}
received = driver->Read(socket, buffer, nbytes); /* AAA */
if(received > 0)
{
nlUpdateSocketInStats(socket, received, 1);
nlUpdateInStats(received, 1);
}
nlUnlockSocket(socket, NL_READ);
return received;
}
}
else
{
nlSetError(NL_INVALID_SOCKET);
}
return NL_INVALID;
}
nlSetError(NL_NO_NETWORK);
return NL_INVALID;
}
Точка, которую я отметил /* AAA */
, вызывает функцию Read
для каждого сетевого стека и просто возвращает любое значение, возвращаемое функцией Read
:
NLint sock_Read(NLsocket socket, NLvoid *buffer, NLint nbytes)
{
nl_socket_t *sock = nlSockets[socket];
NLint count;
if(nbytes < 0)
{
return 0;
}
if(sock->type == NL_RELIABLE || sock->type == NL_RELIABLE_PACKETS) /* TCP */
{
/* I've removed all the code for pending non-blocking connections */
/* check for reliable packets */
if(sock->type == NL_RELIABLE_PACKETS)
{
return sock_ReadPacket(socket, buffer, nbytes, NL_FALSE);
}
count = recv((SOCKET)sock->realsocket, (char *)buffer, nbytes, 0); /* BBB */
if(count == 0)
{
/* end of message */
nlSetError(NL_MESSAGE_END);
return NL_INVALID;
}
}
/* I removed UDP handling */
Часть, отмеченная /* BBB */
, является вызовом системной функции recv()
. Когда система recv()
возвращает 0
(чтобы указать, что узел корректно закрыл сокет), она соответствующим образом устанавливает состояние ошибки и возвращает ошибку.
Исходя из вашего описания проблемы и этого источника, я действительно удивлен. Возврат 0
возможен только через первое if
условие функции sock_Read()
: запрос чтения менее чем 0
байтов или через соединение NL_RELIABLE_PACKETS
, если вы не все же получил весь пакет более высокого уровня данных. Ни один из них не выглядит слишком правдоподобным, поэтому мне очень любопытно увидеть вашу программу.