非阻塞套接字的连接超时问题

qq51931375 2009-09-03 12:56:00
大家好!我以前没注意到这个问题
就是设置非阻塞套接字的连接超时问题,我在网上看到的代码都一样,我现在把这段代码贴出来,请帮忙解惑
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
DWORD 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;
}
第一:如果这端代码是通过select实现的连接超时处理。那为什么把select放在最后,而不是把connect放在select中
第二:如果这端代码是通过select实现的连接超时处理,把为什么在开始要用setsockopt来设置发送超时。
请大家帮我解惑,因为我确实没看出来,上段代码哪个地方有连接超时的处理 100相送 谢谢
...全文
439 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
feiyangdn 2009-09-04
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 qq51931375 的回复:]
还是不懂:
因为:
1.用setsockopt设置了发送超时后,如果send在指定的时间内没发送出去,如何用select获取异常
  通过select(....) == 0?这样来判断?
2.OnRecv这个回调函数和    在一个循环中调用select来轮询socket的状态是否可读是一个原理?
[/Quote]

1、 看看select的第四个参数表示选择fd_set的异常集,这意味着当这个socket集有错误事件发生时,select就会返回,至于什么错误select是不会管的,得看看返回的错误集中的内容。
2、OnReceive是MFC库类中其中一个类的虚函数,至于它怎么实现不是我们要关心的,道理应该是相同的。接收到报文网卡会以硬件中断的方式通知处理器,我的网卡中断请求号是22,所以不一定要轮询,具体怎么做不知道。
月竹影 2009-09-04
  • 打赏
  • 举报
回复
对于非阻塞的连接,设置发送超时和接收超时没有任何作用,应该非阻塞的recv和send都是调用后立即返回,对于recv,如果当前已经有数据到了,那么就接收下来,立刻返回,如果没有数据,也立刻返回,不会等待。
对于send,只是把数据提交出去,如果网卡正空闲,那么数据立刻发送出去,如果网卡正忙,把数据提交后,然后立刻就返回,不会等到数据真正的发送出去才返回。

但是阻塞的话,发送和接收都是等到处理完成才返回,除非出错。比如,send一个数据出去,如果当时网卡很忙,那么这个send一直不返回,一直等到网卡有空把数据发送出去,函数才返回。如果设置了超时,那么如果在设定的超时时间内网卡还没有空,那么这个函数就会返回SOCKET_ERROR,用WSAGetLaseError()函数可以获得错误是WSAETIMEDOUT。

所以在你的例子中,那个超时设置根本就没用。

这样解释你明白不?
qq51931375 2009-09-03
  • 打赏
  • 举报
回复
还是不懂:
因为:
1.用setsockopt设置了发送超时后,如果send在指定的时间内没发送出去,如何用select获取异常
通过select(....) == 0?这样来判断?
2.OnRecv这个回调函数和 在一个循环中调用select来轮询socket的状态是否可读是一个原理?
feiyangdn 2009-09-03
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 shan_dong_ren 的回复:]
刚开始的时候我是做的一台仪器的以太网通信,先把接收到得数据放入链表结构中,测试通信效果还可以,没有丢失数据的现象。现在我要改成多台的,其他的异步IO模型我都不会,看着select比较简单,就尝试着用这种方式,我是用的阻塞socket,结果有丢失数据的现场,我分析了一下原因,可能是采集速度太快,造成数据丢失。不知道是不是这个原因,希望能够得到你的帮助
[/Quote]
send 之后,用WSAGetLastError捕捉异常,关注WSAENOBUFS,WSAEMSGSIZE两个异常类型。
发送一段时间要等待一会儿,等缓冲区的数据发出去后,再接着发。
feiyangdn 2009-09-03
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 qq51931375 的回复:]
既然发送缓冲区满了,信息发不出去了,那为什么还要设置延迟时间(6000ms),而不选择重发?

[/Quote]
重不重发数据不是windows底层所能决定的(这个要和数据重传区别开,参考IP协议),它取决于程序控制。设置延时(6000ms),select对应的消息,就会立即返回,给与用户处理异常的机会,是否选择重发就是用户的事情了。另外设置延时还可以实现回调函数功能,有事件发生时主动进行调用,具体可以查看CAsyncSocket、CSocket使用,不过要在MFC下才能用,重载虚函数OnSend,OnReceive就相当于设置回调函数,可以在OnSend,OnReceive中处理异常。
qq51931375 2009-09-03
  • 打赏
  • 举报
回复
多台电脑发送数据给一台PC的话,请注意那一台PC机的接收缓冲区,有可能是多台主机在发送数据的时候,由于那一台PC的接收缓冲区台小,造成了多余的数据被 PC机丢弃了。
所以建议:
用setsockopt把那一台用于收集信息的主机的接收缓冲区扩大,具体怎么扩大,请参照转换个函数的参数。

然后把每次发送的数据大小由20K下降为8K

然后你再试
shan_dong_ren 2009-09-03
  • 打赏
  • 举报
回复
首先回复你的第四个问题,是用的TCP,从保存的数据可以看出,每一帧数据是连续的(测试用的正弦波),但是帧与帧之间就有断层。
5.我设定的最大发送频率是20k,一次最大发送16384个数据
6.我是一台PC接收多台仪器发送的数据,不是PC给多台仪器发送数据包。
qq51931375 2009-09-03
  • 打赏
  • 举报
回复
首先:
1.阻塞性套接字在处理I/O的时候,不灵活,太慢。
2.select模型在处理小数据量的通讯的时候还可以,但是在处理大数据量的时候就不能用了。因为select内 部的轮询机制本来就不够高效。
3.最好重叠I/O或者WSAEventSelect
4.造成数据丢失的情况,先看看你是不是用的 TCP,如果是测试一下是不是出现了TCP粘包(分几次发送的数据包到了目的后粘成了一个包)。
5.如果你是在WAN上通信的话 ,你每次发送的包是不是有点大 ,最好在4K左右吧
6.你改成了多台了,是不是一台机子给所有机子都 发送同样的数据包,如果是,那就看 是不是发送过程了出现了问题
shan_dong_ren 2009-09-03
  • 打赏
  • 举报
回复
刚开始的时候我是做的一台仪器的以太网通信,先把接收到得数据放入链表结构中,测试通信效果还可以,没有丢失数据的现象。现在我要改成多台的,其他的异步IO模型我都不会,看着select比较简单,就尝试着用这种方式,我是用的阻塞socket,结果有丢失数据的现场,我分析了一下原因,可能是采集速度太快,造成数据丢失。不知道是不是这个原因,希望能够得到你的帮助
qq51931375 2009-09-03
  • 打赏
  • 举报
回复
用非阻塞,而且发送数据很快要注意TCP粘包的问题。
shan_dong_ren 2009-09-03
  • 打赏
  • 举报
回复
还有,你的结贴率太低,善意提醒
shan_dong_ren 2009-09-03
  • 打赏
  • 举报
回复
我也正在select,想请教下楼主,如果我的客户端发送的数据比较快,那我是该用阻塞还是用非阻塞,谢谢
qq51931375 2009-09-03
  • 打赏
  • 举报
回复
既然发送缓冲区满了,信息发不出去了,那为什么还要设置延迟时间(6000ms),而不选择重发?
qq51931375 2009-09-03
  • 打赏
  • 举报
回复
发缓冲区满信息发布出去连续6秒,就会触发一个消息,
什么意思?
lijianli9 2009-09-03
  • 打赏
  • 举报
回复
ret = select(0, 0, &r, 0, &timeout);
第三个参数代表是否有可写的socket,如果连接建立成功,那么表明此socket可写,有数据要发送。
你分清链接,发送,接受的概念,所以设置的函数,参数都不同,
feiyangdn 2009-09-03
  • 打赏
  • 举报
回复
发缓冲区满信息发布不出去持续6秒
feiyangdn 2009-09-03
  • 打赏
  • 举报
回复
1、 以上代码是用Select实现connect连接超时的一种方法,Select实现等待超时,为什么要先connect后select?道理很简单,因为只有connect成功之后socket才触发由不可写到可写的状态转移,仔细看看select第三个参数什么意思(Optional pointer to a set of sockets to be checked for writability ),这只是一种实现方法,用来判断连接成功与否!!
2、 前面设置的读写的超时时间暂时跟select还没有任何关系,因为你这时的connect都没有建立起来,谈不上read和write;连接上之后,发缓冲区满信息发布出去连续6秒,就会触发一个消息,可以通过select捕获这个异常,接受收也有类似情形。

前面的两个6000ms别管它。
skybblue 2009-09-03
  • 打赏
  • 举报
回复
一、只有连接后才能测试连接是否超时,所以在connect后调用select测试连接是否超时
二、setsockopt设置的是连接成功后,发送数据和接收数据的超时,即调用send和recv函数的超时时间。
gotooker 2009-09-03
  • 打赏
  • 举报
回复
1.非阻塞SOCKET是立刻返回的,connect后一般返回WOULDBLOCK,然后如果连接成功的话,SOCKET会有可写的状态,用SELECT判断socket在一定时间内是否可写来实现超时。
2.发送超时和接受超时是针对SEND和RECV的,和连接无关。
mazm_yanzhu 2009-09-03
  • 打赏
  • 举报
回复
学习
加载更多回复(1)

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧