HawkNL库,nlClose不关闭套接字

时间:2011-06-28 21:00:33

标签: delphi sockets tcp

有人可以帮我解决以下问题。

我有一个客户端和服务器tcp可靠连接。

客户端连接到服务器,服务器接收数据包。然后客户端关闭套接字(nlclose) 并且在服务器中nlRead仍然返回0(这意味着仍然存在活动连接) 它没有告诉我客户端关闭套接字,为什么?

只有客户端的nlshutdown强制服务器停止侦听套接字,为什么会这样?

你们有没有人有过hawkNL的经历?

谢谢!

代码在Windows XP Sp3框上运行。

编辑:

客户端代码:

procedure Client;
const
  TCP_PORT = 9800;
  READ_SIZE_CHUNK = 1048576;//1mb
Type
  TChunk = record
    data: pointer;
    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;

服务器代码:

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;

1 个答案:

答案 0 :(得分:1)

您确定自己的API吗? (对于0返回值或有序关闭套接字,documentation I found非常安静。)

当对等方关闭连接时,对TCP堆栈的基础recv()read()调用将返回0。从我的Linux系统上的recv(2)联机帮助页:

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.

请注意,即使在对等体关闭连接之后,套接字中仍可能有少量字节或up to a gigabyte of data或更多数据要读取。在客户端完成后,服务器应用程序可能无法在时间内获得FIN套接字关闭通知,具体取决于管道的延迟和带宽,应用程序的IO策略和内存可用于TCP / IP堆栈以缓冲套接字数据。

下一次 recv()调用(在它返回0之后)将返回错误。我假设套接字的HawkNL接口会遵循这种行为。

此外,nlShutdownshutdown()的通常TCP含义非常不同,后者只是通知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连接。两者似乎都不太合理,所以我很想看到你的程序。