HawkNL library, nlClose doesn't close the socket
Could anyone please help me with the following.
I have a client and server tcp reliable connection.
Client connects to the server, server receives packets. Then Client closes socket (nlclose) and in the server nlRead still returns 0 (which means that there is still active connection) It doesn't inform me that client closed the socket, why?
Only nlshutdown on the client side forces the server to stop listening on the socket, Why is that?
Does anyone of you have an experience with hawkNL?
Thanks!
The code run on Windows XP Sp3 box.
EDIT:
The Client code:
procedure Client;
const
TCP_PORT = 9800;
READ_SIZE_CHUNK = 1048576;//1mb
Type
TChunk = record
data: poin开发者_如何转开发ter;
datasize: NLint;
end;
var
LRslt: NLboolean;
LClientSocket: NLsocket;
LChunkData: TChunk;
LNumRead: Integer;
LReadError: NLenum;
LAddress: NLaddress;
LWrittenBytes: Integer;
LBuffer: array [0..10] of byte;
procedure PopUpMessage(Rslt: NLboolean);
var
LError: NLenum;
LStrRslt: String;
begin
if Rslt
then exit;
LError := nlGetError();
if LError = NL_SYSTEM_ERROR
then LStrRslt := nlGetSystemErrorStr(nlGetSystemError())
else LStrRslt := nlGetErrorStr(error);
showmessage (LStrRslt);
abort;
end;
begin
LReadError := 0;
LRslt := nlInit();PopUpMessage(LRslt);
LRslt := nlSelectNetwork(NL_IP);PopUpMessage(LRslt);
LClientSocket := nlOpen(0, NL_TCP);
if (LClientSocket = NL_INVALID)
then PopUpMessage(false);
nlGetLocalAddr(LClientSocket, LAddress);
//Ustawienie adresu drukarki
LRslt := nlStringToAddr(PChar('127.0.0.1:'+Inttostr(TCP_PORT)), LAddress); PopUpMessage(Lrslt);
//Połączenie z drukarką
LRslt := nlConnect(LClientSocket, LAddress); PopUpMessage(Lrslt);
LWrittenBytes := nlWrite(LClientSocket, LBuffer, 5);
if (LWrittenBytes = NL_INVALID)
then PopUpMessage(false);
//nlClose(LClientSocket);
end;
The Server code:
procedure Serwer;
const
TCP_PORT = 9800;
READ_SIZE_CHUNK = 1048576;//1mb
Type
TChunk = record
data: pointer;
datasize: NLint;
end;
var
LRslt: NLboolean;
LServerSock, LNewSock: NLsocket;
LChunkData: TChunk;
LNumRead: Integer;
LReadError: NLenum;
procedure PopUpMessage(Rslt: NLboolean);
var
LError: NLenum;
LStrRslt: String;
begin
if Rslt
then exit;
LError := nlGetError();
if LError = NL_SYSTEM_ERROR
then LStrRslt := nlGetSystemErrorStr(nlGetSystemError())
else LStrRslt := nlGetErrorStr(error);
showmessage (LStrRslt);
abort;
end;
begin
LReadError := 0;
LRslt := nlInit();PopUpMessage(LRslt);
LRslt := nlSelectNetwork(NL_IP);PopUpMessage(LRslt);
LServerSock := nlOpen(TCP_PORT, NL_RELIABLE);
if (LServerSock = NL_INVALID)
then PopUpMessage(false);
if not nlListen(LServerSock)
then PopUpMessage(false);
repeat
LNewSock := nlAcceptConnection(LServerSock);
if LNewSock <> NL_INVALID then
begin
GetMem (LChunkData.data, READ_SIZE_CHUNK);
repeat
LNumRead := nlRead(LNewSock, LChunkData.data^, READ_SIZE_CHUNK);
//Here I am receiving 0 bytes reads from LNumRead....
if LNumRead > 0 then
begin
beep;
//I am receiving stream, do nothing with that
end else
begin
if LNumRead = NL_INVALID then
begin
LReadError := nlGetError();
if (LReadError = NL_INVALID_SOCKET) or
(LReadError = NL_MESSAGE_END)
then PopUpMessage(false);
end;
end;
until LReadError <> 0;//(LReadError = NL_NO_PENDING)
beep;//Here it should exit if all would work ok.
end else
begin
if nlGetError() = NL_SYSTEM_ERROR
then PopUpMessage(false);
end;
until 1=2;
beep;
end;
Are you certain of your API? (The documentation I found is remarkably quiet about a 0
return value or orderly closure of the socket.)
The underlying recv()
or read()
calls to the TCP stack return 0
when the peer has closed the connection. From the recv(2)
manpage on my Linux system:
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.
Note that there might a few bytes or up to a gigabyte of data or more data left in the socket to read even after a peer has closed a connection. You server application might not get the FIN
socket-closed notification for a long time after the client is done, depending upon the latency and bandwidth of your pipes, your application's IO strategy, and memory available to the TCP/IP stack for buffering socket data.
The next recv()
call (after it has once returned 0
) will return an error. I assume the HawkNL interface to sockets would follow this behavior.
Furthermore, nlShutdown
appears very different from the usual TCP meaning of shutdown()
, which simply informs the TCP stack that the application is done reading, writing, or reading and writing, on a single specific socket. Using it in your client might be a little draconian.
Edit
I've looked over the HawkNL source a little, and now I'm even more confused about the behavior you're seeing.
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;
}
The point I've marked /* AAA */
calls into a per-networking-stack Read
function, and simply returns whatever value comes back from the Read
function:
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 */
The portion marked with /* BBB */
is the call to the system recv()
function. When the system recv()
returns 0
(to indicate the peer has closed the socket gracefully), it sets the error state appropriately and returns an error.
Based on your description of the problem, and this source, I'm really surprised. A 0
return looks only possible via the first if
condition of the sock_Read()
function: requesting a read of less than 0
bytes or via a NL_RELIABLE_PACKETS
connection when you haven't yet received all of a higher-level packet of data. Neither one seems too plausible, so I'm really curious to see your program.
精彩评论