关于tcp/ip连接的一个非常底层的问题。(100分)

T

tonyyu

Unregistered / Unconfirmed
GUEST, unregistred user!
无论是使用delphi还是vc开发socket客户服务程序都会遇到这样一个问题。
客户端socket,连接服务socket,系统确省绑定1024~5000之内的一个port,但是当连接释放之后,在命令行下
输入netstat -an察看本机所有的tcp连接,发现被关掉的socket还在,如果此时重新连接,又会重新申请一个
端口,先前的状态为time_wait,一直要等待5分钟左右,才会释放被占用的端口。
如果连接比较频繁的话,会使得系统没有可以分配的port,并且很多socket time_wait也很消耗
系统资源,我不想要time_wait状态,希望能够马上释放掉这个连接,不知道如何处理。
还请各位高人指点。
 
我觉得这是一个非常有意思的问题,我使着写了一下:)
closesocket语句的执行效果是与“套接字选项(socket option)”的设置相关的,具体的
是受SO_LINGER 和 SO_DONTLINGER影响效果为(通过查MSDN得到的):
Option Interval Type of close Wait for close?
SO_DONTLINGER Do not care Graceful No
SO_LINGER Zero Hard No
SO_LINGER Nonzero Graceful Yes

我们在关闭套接字时为了处理还未接受到的数据,所以不马上关闭连接(hard方式)
当然你可以强行关闭连接(即以hard方式)。套接字选项(socket option)可以通过方法
setsockopt来设置,该方法在delphi中的原型为:
function setsockopt(s: TSocket; level, optname: Integer; optval: PChar;
optlen: Integer): Integer;
你如果想在连接关闭后马上关闭连结则需设置SO_LINGER的间隔(Interval)为0就可以了
如:
var
t:linger;

t.l_onoff := 1;
t.l_linger := 0;
Re := setsockopt(sckt,SOL_SOCKET,SO_LINGER,@t,sizeof(t)) ;

我写的测试的例子如下,可能乱了一点:

procedure TForm1.Button1Click(Sender: TObject);
var
sckt:TSocket;
WSAData:TWSAData;
addr:TSockAddr;
Re,err:Integer;
Info:string;
t : linger;
begin
if (WSAStartup(MAKEWORD(2,0),WSAData)<>0)then
begin
memo1.Lines.Add('Winsock Init Failed');
exit;
end
else
memo1.Lines.Add('Socket Start');
//-----------------//
sckt:=socket(AF_INET,SOCK_STREAM,0);
if(sckt=INVALID_SOCKET)then
begin
memo1.Lines.Add('Erro:Create socket failed!');
exit;
end;
//--------设置套接字选项-------//
t.l_onoff := 1;
t.l_linger := 0;
Re := setsockopt(sckt,SOL_SOCKET,SO_LINGER,@t,sizeof(t)) ;
//----------以下是相应的错误信息,是直接从MSDN中粘过来的,有点乱------//
if Re = SOCKET_ERROR then
begin
err:=WSAGetLastError();
case err of
WSANOTINITIALISED : Info:='A successful WSAStartup call must occur before using this function.';
WSAENETDOWN : Info:='The network subsystem has failed. ';
WSAEFAULT : Info:='The optval parameter is not in a valid part of the process address space or the optlen parameter is too small. ';
WSAEINPROGRESS : Info:='A blocking Winsock call is in progress, or the service provider is still processing a callback function. ';
WSAEINVAL : Info:='The level parameter is not valid, or the information in optval is not valid. ';
WSAENETRESET : Info:='The connection has timed out when SO_KEEPALIVE is set. ';
WSAENOPROTOOPT : Info:='The option is unknown or unsupported for the specified provider or the socket ';
WSAENOTCONN : Info:='The connection has been reset when SO_KEEPALIVE is set';
WSAENOTSOCK : Info:='The descriptor is not a socket';
end;
end
else
Info:='Set socket option ok!';
memo1.Lines.Add(Info);
//------------//
ZeroMemory(@addr,sizeof(addr));
addr.sin_family:=AF_INET;
addr.sin_addr.S_addr :=inet_addr(Pchar(edit1.Text));
addr.sin_port :=htons(StrToInt(edit2.Text));
Re:=connect(sckt,addr,sizeof(addr));
if(Re<>0)then
begin
memo1.Lines.Add('Connect to server failed');
exit;
end
else
Memo1.Lines.Add('Connect to server Success');
//----------关闭套接字--------//
Re:=closesocket(sckt);
if Re=SOCKET_ERROR then
begin
err:=WSAGetLastError();
case err of
WSANOTINITIALISED : Info:='A successful WSAStartup call must occur before using this function.';
WSAENETDOWN : Info:='The network subsystem has failed. ';
WSAEFAULT : Info:='The optval parameter is not in a valid part of the process address space or the optlen parameter is too small. ';
WSAEINPROGRESS : Info:='A blocking Winsock call is in progress, or the service provider is still processing a callback function. ';
WSAEINTR : Info:='Canceled through WSACancelBlockingCall';
WSAEWOULDBLOCK : Info:='time-out value to large';
end;
end
else
Info:='Close socket success';
memo1.Lines.Add(Info);
WSACleanUP();

end;

当运行上面的例子时,你再用netstat -an来试一下会发现socket是立刻关闭的,如果设置选项为
SOL_DONTLINGER或关闭LINGER(通过设置t.l_onoff=0)则会发想socket会继续等待。并且通过
上面对套接字选项SO_LINGER的说明我们还可以设置wait的时间,如等待1000秒:
t.l_onoff := 1;
t.l_linger := 1000;
//--------设置套接字选项-------//
Re := setsockopt(sckt,SOL_SOCKET,SO_LINGER,@t,sizeof(t)) ;
关于这个等待时间的设置是有问题的,因为通过测试发现与MSDN中的说明不符,好像不起作用.
 
不是不起作用,你应该先读取系统默认时间,在来修改哪个值。同时写回TSOCKET里面
应该就可以了
 
佩服,佩服
 
这个参数应该是一个char *类型,怎么是这样呢?
把他设为1就没问题了,一直都是这样做的。
 
to chenxz
>>这个参数应该是一个char *类型,怎么是这样呢?
当然setsockopt(sckt,SOL_SOCKET,SO_LINGER,@t,sizeof(t))也可以写成
setsockopt(sckt,SOL_SOCKET,SO_LINGER,pchar(@t),sizeof(t)) ;
>>把他设为1就没问题了,一直都是这样做的。
设置成整数1当然没有问题,也就是可以用下面这样的语句:
i : Integer;
i := 1;
setsockopt(sckt,SOL_SOCKET,SO_LINGER,pchar(@i),sizeof(i)) ;
因为它等同于
t.l_onoff := 1;//l_onoff为u_short即word
t.l_linger := 0;//l_onoff为u_short即word
因为有声明:
linger = record
l_onoff: u_short;
l_linger: u_short;
end;
所以说如果把i设置为65536即相当于设置t为:
t.l_onoff := 0;//l_onoff为u_short即word
t.l_linger := 1;//l_onoff为u_short即word
会发现连接不会马上关闭的,所以说对于套接字选项SO_LINGER,setsockopt中的optval
参数是有多种设置的。
 
设置上面的参数以后,就可以开多线程扫描,不用担心系统资源消耗了
 
以前对于linger参数一直没有花时间研究,谢谢awl的指点,我try之后OK。
 
顶部