开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜