有人可以帮我解决以下问题。
我有一个客户端和服务器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;
答案 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接口会遵循这种行为。
此外,nlShutdown
与shutdown()
的通常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
连接。两者似乎都不太合理,所以我很想看到你的程序。