大家好!我以前没注意到这个问题
就是设置非阻塞套接字的连接超时问题,我在网上看到的代码都一样,我现在把这段代码贴出来,请帮忙解惑
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相送 谢谢 

解决方案 »

  1.   

    ret = select(0, 0, &r, 0, &timeout); 
    if ( ret <= 0 ) 

    ::closesocket(cClient); 
    return 0; 
    } select()第三个参数是超时,ret <= 0表示有错误发生或超时。
    select只是等待连接,所以要先连接(connect),后等待
      

  2.   

    1.非阻塞SOCKET是立刻返回的,connect后一般返回WOULDBLOCK,然后如果连接成功的话,SOCKET会有可写的状态,用SELECT判断socket在一定时间内是否可写来实现超时。
    2.发送超时和接受超时是针对SEND和RECV的,和连接无关。
      

  3.   

    一、只有连接后才能测试连接是否超时,所以在connect后调用select测试连接是否超时
    二、setsockopt设置的是连接成功后,发送数据和接收数据的超时,即调用send和recv函数的超时时间。
      

  4.   

    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别管它。
      

  5.   

    ret = select(0, 0, &r, 0, &timeout); 
    第三个参数代表是否有可写的socket,如果连接建立成功,那么表明此socket可写,有数据要发送。
    你分清链接,发送,接受的概念,所以设置的函数,参数都不同,
      

  6.   

    我也正在select,想请教下楼主,如果我的客户端发送的数据比较快,那我是该用阻塞还是用非阻塞,谢谢
      

  7.   

    用非阻塞,而且发送数据很快要注意TCP粘包的问题。
      

  8.   

    刚开始的时候我是做的一台仪器的以太网通信,先把接收到得数据放入链表结构中,测试通信效果还可以,没有丢失数据的现象。现在我要改成多台的,其他的异步IO模型我都不会,看着select比较简单,就尝试着用这种方式,我是用的阻塞socket,结果有丢失数据的现场,我分析了一下原因,可能是采集速度太快,造成数据丢失。不知道是不是这个原因,希望能够得到你的帮助
      

  9.   

    首先:
    1.阻塞性套接字在处理I/O的时候,不灵活,太慢。
    2.select模型在处理小数据量的通讯的时候还可以,但是在处理大数据量的时候就不能用了。因为select内 部的轮询机制本来就不够高效。
    3.最好重叠I/O或者WSAEventSelect
    4.造成数据丢失的情况,先看看你是不是用的 TCP,如果是测试一下是不是出现了TCP粘包(分几次发送的数据包到了目的后粘成了一个包)。
    5.如果你是在WAN上通信的话 ,你每次发送的包是不是有点大 ,最好在4K左右吧
    6.你改成了多台了,是不是一台机子给所有机子都 发送同样的数据包,如果是,那就看 是不是发送过程了出现了问题
      

  10.   

    首先回复你的第四个问题,是用的TCP,从保存的数据可以看出,每一帧数据是连续的(测试用的正弦波),但是帧与帧之间就有断层。
    5.我设定的最大发送频率是20k,一次最大发送16384个数据
    6.我是一台PC接收多台仪器发送的数据,不是PC给多台仪器发送数据包。
      

  11.   

    多台电脑发送数据给一台PC的话,请注意那一台PC机的接收缓冲区,有可能是多台主机在发送数据的时候,由于那一台PC的接收缓冲区台小,造成了多余的数据被 PC机丢弃了。
    所以建议:
    用setsockopt把那一台用于收集信息的主机的接收缓冲区扩大,具体怎么扩大,请参照转换个函数的参数。然后把每次发送的数据大小由20K下降为8K然后你再试
      

  12.   

    重不重发数据不是windows底层所能决定的(这个要和数据重传区别开,参考IP协议),它取决于程序控制。设置延时(6000ms),select对应的消息,就会立即返回,给与用户处理异常的机会,是否选择重发就是用户的事情了。另外设置延时还可以实现回调函数功能,有事件发生时主动进行调用,具体可以查看CAsyncSocket、CSocket使用,不过要在MFC下才能用,重载虚函数OnSend,OnReceive就相当于设置回调函数,可以在OnSend,OnReceive中处理异常。
      

  13.   

    send 之后,用WSAGetLastError捕捉异常,关注WSAENOBUFS,WSAEMSGSIZE两个异常类型。
    发送一段时间要等待一会儿,等缓冲区的数据发出去后,再接着发。
      

  14.   

    还是不懂:
    因为:
    1.用setsockopt设置了发送超时后,如果send在指定的时间内没发送出去,如何用select获取异常
      通过select(....) == 0?这样来判断?
    2.OnRecv这个回调函数和    在一个循环中调用select来轮询socket的状态是否可读是一个原理?
      

  15.   


    1、 看看select的第四个参数表示选择fd_set的异常集,这意味着当这个socket集有错误事件发生时,select就会返回,至于什么错误select是不会管的,得看看返回的错误集中的内容。
    2、OnReceive是MFC库类中其中一个类的虚函数,至于它怎么实现不是我们要关心的,道理应该是相同的。接收到报文网卡会以硬件中断的方式通知处理器,我的网卡中断请求号是22,所以不一定要轮询,具体怎么做不知道。
      

  16.   

    对于非阻塞的连接,设置发送超时和接收超时没有任何作用,应该非阻塞的recv和send都是调用后立即返回,对于recv,如果当前已经有数据到了,那么就接收下来,立刻返回,如果没有数据,也立刻返回,不会等待。
    对于send,只是把数据提交出去,如果网卡正空闲,那么数据立刻发送出去,如果网卡正忙,把数据提交后,然后立刻就返回,不会等到数据真正的发送出去才返回。但是阻塞的话,发送和接收都是等到处理完成才返回,除非出错。比如,send一个数据出去,如果当时网卡很忙,那么这个send一直不返回,一直等到网卡有空把数据发送出去,函数才返回。如果设置了超时,那么如果在设定的超时时间内网卡还没有空,那么这个函数就会返回SOCKET_ERROR,用WSAGetLaseError()函数可以获得错误是WSAETIMEDOUT。所以在你的例子中,那个超时设置根本就没用。这样解释你明白不?