//这是一个服务器端的recv程序,我想实现的功能就是当缓冲区没有数据的时候,按照设定的超时等待数据到来
//Conn为accept返回的SOCKET
//现在的问题就是select返回1,但是recv却没有收到数据
BYTE tmpBuf[20] = {0};
UINT ret = 0; TIMEVAL tv;
tv.tv_sec = 12;//超时设置为12S
tv.tv_usec = 0;

    fd_set fdread;
    FD_ZERO(&fdread);
    FD_SET(Conn, &fdread);

    ret = select(0, &fdread, NULL, NULL, &tv);//select第一个参数设置为Conn也没用
    if (ret == SOCKET_ERROR) 
{
cout<<"select recv socket error"<<endl;

return FALSE;
    }

    if ((ret > 0) && (FD_ISSET(Conn, &fdread))) 
{
//这里执行到了,返回的ret却等于0
//第一次recv是可以的,第二次recv就出现这种现象了
//如果我加断点等待的话,通过监控软件,是可以看到数据到来的,在我上面设定的12S超时之内到来的,这时候再recv就可以了
//问题就是,为什么select返回1,但是数据却并没有到来,recv失败了
ret = recv(Conn,(char*)tmpBuf,4,0);

cout<<"recv = " << ret <<endl;
if(ret != 4)
{
cout<<"recv command length error!"<<endl; int er = WSAGetLastError(); cout<< "er = "<<er<<endl;
return FALSE;
}
else
{ UINT nLen = (tmpBuf[0] << 24) + (tmpBuf[1] << 16) + 
   (tmpBuf[2] << 8) + tmpBuf[3]; ret = recv(Conn,&recvBuf[4],nLen,0);
if(ret != nLen)
{
cout<<"recv command error!"<<endl; return FALSE;
}
else
{
return TRUE;
}
}
    }
else
{    
cout<<"select recv timeout!"<<endl;
return FALSE;
}

解决方案 »

  1.   

    BOOL WINAPI Open()
    {
    WSADATA wsaData;
    int status;

    status = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (status != 0) 
    {
    cout<<"WSAStartup error!"<<endl; return FALSE;
    } if (LOBYTE(wsaData.wVersion) != 2 ||
            HIBYTE(wsaData.wVersion) != 2 ) 
    {
    cout<<"WSAData version error!"<<endl;

    WSACleanup();
    return FALSE;
    } Socket = socket(AF_INET,SOCK_STREAM,/*IPPROTO_TCP*/0);
    if(Socket == INVALID_SOCKET)
    {
    cout<<"socket error!"<<endl; return FALSE;
    } int sendTimeout = 5000;
    if(setsockopt (Socket,SOL_SOCKET,SO_SNDTIMEO,(char*)&sendTimeout,sizeof(int)) == SOCKET_ERROR)
    {
    cout<<"set send timeout error!"<<endl; return FALSE;
    } int recvTimeout = 5000;
    if(setsockopt (Socket,SOL_SOCKET,SO_SNDTIMEO,(char*)&recvTimeout,sizeof(int)) == SOCKET_ERROR)
    {
    cout<<"set recv timeout error!"<<endl; return FALSE;
    } BOOL isOn = TRUE;
    if(setsockopt (Socket,SOL_SOCKET,SO_REUSEADDR,(char*)&isOn,sizeof(isOn)) == SOCKET_ERROR)
    {
    cout<<"set SO_REUSEADDR error!"<<endl; return FALSE;
    } if(setsockopt (Socket,IPPROTO_TCP,TCP_NODELAY,(char*)&isOn,sizeof(isOn)) == SOCKET_ERROR)
    {
    cout<<"set TCP_NODELAY error!"<<endl; return FALSE;
    }

    SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(_PORT); status = bind(Socket,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
    if (status != 0) 
    {
    cout<<"bind error!"<<endl; return FALSE;
    } status = listen(Socket,5);
    if (status != 0) 
    {
    cout<<"listen error!"<<endl; return FALSE;
    }

    return TRUE;
    }
      

  2.   

    void WINAPI WaitForConnect()
    {
    SOCKADDR_IN addrClient;
    int len = sizeof(SOCKADDR); Conn = accept(Socket,(SOCKADDR*)&addrClient,&len);
    if(Conn == INVALID_SOCKET)
    {
    cout<<"accept error!"<<endl;
    }
    else
    {
    char recvBuf[20] = {0};
    int status; status = recv(Conn,recvBuf,20,0);
    if(status < 1)
    {
    cout<<"recv error!"<<endl;
    }
    else
    {

    }
    }
    }
      

  3.   

    从上面代码中没看出问题,可能与客户端程序有关。可以先把setsockopt都注释掉看看是否正常。另外发现一点小问题,就是你设置socket发送和接收超时用的都是SO_SNDTIMEO。
      

  4.   

    int recvTimeout = 5000; 
    if(setsockopt (Socket,SOL_SOCKET,SO_SNDTIMEO,(char*)&recvTimeout,sizeof(int)) == SOCKET_ERROR) 

    cout < <"set recv timeout error!" < <endl; return FALSE; 

    感觉应该是 
    SO_RCVTIMEO
      

  5.   

    就是说,客户端是一个exe,没有源码,也无法更改他
    肯定是可以实现多次recv的,因为见过其它的软件实现过
    我们现在就是想模仿那个软件
      

  6.   

    你的程序是不是多线程?有没有其它线程使用Conn?
      

  7.   

    没有多线程主楼是recv代码
    1楼是bind绑定端口代码
    2楼是等待客户端连接到来代码运行顺序是1楼,2楼,客户端连接之后,和客户端进行收发操作,主楼就是其中收的操作
      

  8.   

    先把setsockopt都注释掉看看是否正常,另外最好再换台电脑试试。
      

  9.   

    那么你的程序是只考虑一个客户的连接,也就是Conn只有一个值对吗?2楼之后是怎么转入主楼的代码的?我的意思是中间是否有改变Conn的操作。因为按照你的描述,只有一种可能,就是select中的Conn连接被断开或者处于listen状态。
      

  10.   

    这个代码没问题才怪,fdset集合每次调用select都会改变,只有有数据到来的套接字才在里面,你每次调用select后都应该把fdset集合重新初始化一次,把你感兴趣的事件和套接字设进去。然后select。