经验分享1: connect 、recv、send 超时解决方案 ( 积分: 0 )

  • 主题发起人 主题发起人 leaber
  • 开始时间 开始时间
L

leaber

Unregistered / Unconfirmed
GUEST, unregistred user!
好多朋友在问这个问题,希望我的文档能对大家有所帮助~~!!

CONNECT 连接超时解决方案
和send recv 超时解决方案
leaber
2004-10-12整理测试

做网络程序中会经常见到判断CONNECT超时的问题,解决的思路是这样的,
首先把要连接服务器的SOCKET转换为非阻塞方式后进行连接,然后利用
时间差值来判断是否超时。
如果成功的话就把SOCKET再设为阻塞方式,当然如果超时的话你就看着办吧!
时间差值的计算方式有好多种,我的实现方式是以SELECT来实现的.

===============这里是我成功测试的代码==========================

Skt := Socket(AF_INET, 1, 0);
ca.sin_family := AF_INET;
ca.sin_port := htons(80);

FD_ZERO(fs);
FD_SET(skt, fs);
timeout.tv_sec := 10;
timeout.tv_usec := 500;

//在这里完成CONNECT超时检查的功能
ulB := 1;
ioctlsocket(Skt, FIONBIO, ulB);
if Connect(skt, ca, sizeof(ca)) = SOCKET_ERROR then
begin
if (WSAGetLastError() = WSAEWOULDBLOCK) then
begin
FD_ZERO(fs);
FD_SET(skt, fs);
rc := select(0, nil, @fs, nil, @Timeout);
end;
end;
if rc = 0 then
begin
//ShowMessage('connect overtime!');
CloseSocket(skt);
exit;
end;
ulB := 0;
ioctlsocket(Skt, FIONBIO, ulB);
//CONNECT 超时功能结束

通过SETSOCKOPT可以完成对RECV和SEND相应的超时设置!

//完成SETSOCKOPT 进行相应的发送和接收的超时
timeout1 := 5000;
if SetSockOpt(skt, SOL_SOCKET, SO_SNDTIMEO, pchar(@TimeOut1), SizeOf(TimeOut1)) <> 0
then
Exit;
if SetSockOpt(skt, SOL_SOCKET, SO_RCVTIMEO, pchar(@TimeOut1), SizeOf(TimeOut1)) <> 0
then
Exit;
 
好多朋友在问这个问题,希望我的文档能对大家有所帮助~~!!

CONNECT 连接超时解决方案
和send recv 超时解决方案
leaber
2004-10-12整理测试

做网络程序中会经常见到判断CONNECT超时的问题,解决的思路是这样的,
首先把要连接服务器的SOCKET转换为非阻塞方式后进行连接,然后利用
时间差值来判断是否超时。
如果成功的话就把SOCKET再设为阻塞方式,当然如果超时的话你就看着办吧!
时间差值的计算方式有好多种,我的实现方式是以SELECT来实现的.

===============这里是我成功测试的代码==========================

Skt := Socket(AF_INET, 1, 0);
ca.sin_family := AF_INET;
ca.sin_port := htons(80);

FD_ZERO(fs);
FD_SET(skt, fs);
timeout.tv_sec := 10;
timeout.tv_usec := 500;

//在这里完成CONNECT超时检查的功能
ulB := 1;
ioctlsocket(Skt, FIONBIO, ulB);
if Connect(skt, ca, sizeof(ca)) = SOCKET_ERROR then
begin
if (WSAGetLastError() = WSAEWOULDBLOCK) then
begin
FD_ZERO(fs);
FD_SET(skt, fs);
rc := select(0, nil, @fs, nil, @Timeout);
end;
end;
if rc = 0 then
begin
//ShowMessage('connect overtime!');
CloseSocket(skt);
exit;
end;
ulB := 0;
ioctlsocket(Skt, FIONBIO, ulB);
//CONNECT 超时功能结束

通过SETSOCKOPT可以完成对RECV和SEND相应的超时设置!

//完成SETSOCKOPT 进行相应的发送和接收的超时
timeout1 := 5000;
if SetSockOpt(skt, SOL_SOCKET, SO_SNDTIMEO, pchar(@TimeOut1), SizeOf(TimeOut1)) <> 0
then
Exit;
if SetSockOpt(skt, SOL_SOCKET, SO_RCVTIMEO, pchar(@TimeOut1), SizeOf(TimeOut1)) <> 0
then
Exit;
 
老兄啊,能否给个完整的函数阿?

延时可以自定义的那种。
 
回头我把我那贴结束了,分数全部给你
 
不是吧,大家都没什么反应!
 
FD_ZERO(fs);
FD_SET(skt, fs);

fs:应该怎么定义?
 
var
fs:TFDSet;
skt:TSocket;
ca:TSockAddrIn;
 
fd_set rset; //定义描述字集数据类型

FD_ZERO (&rset); //对描述字集初始化

知道了
但是下面的???是怎么回事?


timeout.tv_sec := 10;
timeout.tv_usec := 500;
 
timeout:
找到了,嘿嘿,但
timeout.tv_usec := 500; 这句话的作什么用呢?
struct timeval {
int tv_sec;
int tv_usec;
};

其中tv_sec是由凌晨开始算起的秒数,tv_usec则是微秒(10E-6 second)。
 
指定超时的时间
 
Socket中如何设置连接超时
2004-05-27 antghazi

  设置connect的超时很简单,CSDN上也有人提到过使用select,但却没有一个令人满意与完整的答案。偶所讲的也正是select函数,此函数集成在winsock1.1中,简单点讲,"作用使那些想避免在套接字调用过程中被锁定的应用程序,采取一种有序的方式,同时对多个套接字进行管理" (《Windows网络编程技术》原话)。使用方法与解释请见《Windows网络编程技术》。
  在使用此函数前,需先将socket设置为非锁定模式,这样,在connect时,才会立马跳过,同时,通常也会产生一个WSAEWOULDBLOCK错误,这个错误没关系。再执行select则是真正的超时。

WSADATA wsd;
SOCKET cClient;
int ret;
struct sockaddr_in server;
hostent *host=NULL;

if(WSAStartup(MAKEWORD(2,0),&wsd)){return 0;}
cClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(cClient==INVALID_SOCKET){return 0;}
//set Recv and Send time out
int TimeOut=6000; //设置发送超时6秒
if(::setsockopt(cClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){
return 0;
}
TimeOut=6000;//设置接收超时6秒
if(::setsockopt(cClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){
return 0;
}
//设置非阻塞方式连接
unsigned long ul = 1;
ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul);
if(ret==SOCKET_ERROR)return 0;

//连接
server.sin_family = AF_INET;
server.sin_port = htons(25);
server.sin_addr .s_addr = inet_addr((LPCSTR)pSmtp);
if(server.sin_addr.s_addr == INADDR_NONE){return 0;}

connect(cClient,(const struct sockaddr *)&server,sizeof(server));

//select 模型,即设置超时
struct timeval timeout ;
fd_set r;

FD_ZERO(&r);
FD_SET(cClient, &r);
timeout.tv_sec = 15; //连接超时15秒
timeout.tv_usec =0;
ret = select(0, 0, &r, 0, &timeout);
if ( ret <= 0 )
{
::closesocket(cClient);
return 0;
}
//一般非锁定模式套接比较难控制,可以根据实际情况考虑 再设回阻塞模式
unsigned long ul1= 0 ;
ret = ioctlsocket(cClient, FIONBIO, (unsigned long*)&ul1);
if(ret==SOCKET_ERROR){
::closesocket (cClient);
return 0;
 
呵呵,道理是一样的!
 
楼主帮我看看这个函数为什么不正确(连接结果不对)

function TcpConnection(_Host:String;_Port:Integer;_TimeOut: integer):integer;
var i:integer;
sin:sockaddr_in;
mint :integer ;
len :integer;
buf:array[0..1] of char;
value:string;
fs: TFDSet;
timeout: timeval;
ulb: integer;
RC:integer;
const s:string='0009';
begin
sin.sin_family:=AF_INET;
sin.sin_port:=htons(_Port);
i:=inet_addr(PChar(_Host));
sin.sin_addr.S_addr:=i;
mint := 0;
FD_ZERO(fs);

try
mint := socket(PF_INET,SOCK_STREAM,0);
FD_SET(mint,fs);
timeout.tv_sec := _TimeOut;//连接超时
timeout.tv_usec := 500;
ulb := 1;
ioctlsocket(mint,FIONBIO,ulb);
// Connect(mint,sin,sizeof(sin));
if Connect(mint,sin,sizeof(sin))=SOCKET_ERROR then
begin//
if (WSAGetLastError() = WSAEWOULDBLOCK) then
begin
FD_ZERO(fs);
FD_SET(mint,fs);
RC := select(0,nil,@fs,nil,@TimeOut);
end;
if RC = 0 then
Result := 0
else
Result := 1;
end;//
finally
ulb := 0;
ioctlsocket(mint,FIONBIO,ulb);
CloseSocket(mint);
end;
end;
 
有什么问题
 
问题: 不能正确获得连接结果(可以ping通:192.168.1.1但是用这个函数得出的结果却是不通Result为0)
TcpConnection(192.168.1.1,80,10)
返回的结果是0
可我在cmd里面ping 192.168.1.1 是通的阿
 
首先必须在192。168。1。1上有服务端才可以连通,还有就是你指定的时间的单位是MS,所以长一点,TcpConnection(192.168.1.1,80,10000)
 
leaber:
你好,你有qq或者MSN吗?

这样问你效率太慢了:)
我的msn:
doghub@msn.com
 
leaber:
你好,经过测试 函数TcpConnection的结果是不全部正确的。
测试代码:选一个可以连通的ip+port('192.168.253.1',86)
j := 0;
k := 0;
for i := 0 to 199 do
begin
k := k + TcpConnection2('192.168.253.1',86);
j := j + TcpConnection('192.168.253.232',86,1);
//延时1秒????这里的1是微秒吗?
end;
ShowMessage('TcpConnection Success Count: '+IntToStr(j)
+#13+
'TcpConnection2SuccessCount '+inttostr(k));

TcpConnection Success Count:125
TcpConnection2 SuccessCount:200


附:
TcpConnection2,这个在连接不通的情况下要延时20多秒(另外一个TcpConnection就是对此改进:可以自定义延时时间)


function TcpConnection2(_Host:String;_Port:Integer):integer;
var i:integer;
sin:sockaddr_in;
mint :integer ;
begin
sin.sin_family:=AF_INET;
sin.sin_port:=htons(_Port);
i:=inet_addr(PChar(_Host));
sin.sin_addr.S_addr:=i;
mint := 0;
try
mint := socket(PF_INET,SOCK_STREAM,0);
if Connect(mint,sin,sizeof(sin))=SOCKET_ERROR then
Result := 0
else
Result :=1 ;
finally
CloseSocket(mint);
end;
end;



function TcpConnection(_Host:String;_Port:Integer;_TimeOut: integer):integer;
var i:integer;
sin:sockaddr_in;
mint :integer ;
fs: TFDSet;
timeout: timeval;
ulb: integer;
RC:integer;
begin
sin.sin_family:=AF_INET;
sin.sin_port:=htons(_Port);
i:=inet_addr(PChar(_Host));
sin.sin_addr.S_addr:=i;
mint := 0;
FD_ZERO(fs);

try
mint := socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
FD_SET(mint,fs);
timeout.tv_sec := _TimeOut;//连接超时
//延时1秒????这里的1是微秒吗?
//问题是不是设置的微秒太少?导致连接失败?
//long tv_sec; /*秒*/ long tv_usec; /*微秒*/
timeout.tv_usec := 500;
ulb := 1;
ioctlsocket(mint,FIONBIO,ulb);
if Connect(mint,sin,sizeof(sin))=SOCKET_ERROR then
begin//
if (WSAGetLastError() = WSAEWOULDBLOCK) then
begin
FD_ZERO(fs);
FD_SET(mint,fs);
RC := select(0,nil,@fs,nil,@TimeOut);
end;
if RC = 0 then
Result := 0
else
Result := 1;
end// 返回的Result 有问题???????
else
Result := 1;
finally
ulb := 0;
ioctlsocket(mint,FIONBIO,ulb);
CloseSocket(mint);
end;
end;
 
如果超时会有路由不可达的错误返回.不用这样麻烦吧!
 
后退
顶部