我使用blocking mode的socket,在接收数据时,第一次调用recv会成功,但
只能收到一部分数据,再次调用recv()接收剩余数据时,出错,返回错误是10038
也就是无效socket,但我保证socket没有错。我甚至尝试按异步socket处理,在接收前加上下面这样一段代码来确保有数据可接收都没用!
 这是为什么??????????????????

解决方案 »

  1.   

    加入的等待代码是:
    HANDLE hEvent = WSACreateEvent();
    if (hEvent != WSA_INVALID_EVENT)
    {
    if (WSAEventSelect(sckHTTPSocket, hEvent,  FD_READ) == 0 &&
    WSAWaitForMultipleEvents(1, &hEvent, FALSE, m_nRecvTimeout, FALSE) != WAIT_OBJECT_0)
    {
    free(pBuffer);
    return NULL;
    }
    CloseHandle(hEvent);
    }
      

  2.   

    确保你的send成功。
    你是不是在send成功后立即close了socket?
      

  3.   

    当你send完了,那么就recv,要是用blocking mode,等到那边也recv完了,那么在send一下,两边同时安全关闭socket
      

  4.   

    是有事件的原因,是用了多线程吧,用临界区控制一下吧,我PING的时候的代码你看看:
    bool CUDPPing::Ping(LPCTSTR pStrHostName, DWORD nPingTimeout)
    { SOCKET   rawSocket;
      struct    sockaddr_in saDest;
      struct    sockaddr_in saSrc;
      LPHOSTENT lpHost;
      CString str;
      int nRet;

      DWORD   dwTimeSent,dwElapsed;
      u_char    cTTL;
    long m_laddr;
    //清零  rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    while (TRUE)
    {
    m_laddr = inet_addr((CString)pStrHostName);
    if (m_laddr == INADDR_NONE)
    {

    lpHost = gethostbyname((CString)pStrHostName);
      if (lpHost == NULL)
      {
     
    continue;
    }
      saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));
    }
    else
    {
    saDest.sin_addr.s_addr =m_laddr;
    }
      saDest.sin_family = AF_INET;
      saDest.sin_port = 0;
     
          
      //PING

      SendEchoRequest(rawSocket, &saDest);
      nRet = WaitForEchoReply(rawSocket);
      if (!nRet)
      {

      return FALSE;
     
      }
      else
      {
      // 刷新回应
      dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);
    // 计算剩余时间
      dwElapsed = GetTickCount() - dwTimeSent;
     
      }

    }
    nRet = closesocket(rawSocket); return TRUE;
    }int CUDPPing::WaitForEchoReply(SOCKET s)
    { struct timeval Timeout;
    fd_set readfds; readfds.fd_count = 1;
    readfds.fd_array[0] = s;
    Timeout.tv_sec = 1;
        Timeout.tv_usec = 0;
    return(select(1, &readfds, NULL, NULL, &Timeout));
    }int CUDPPing::SendEchoRequest(SOCKET s, LPSOCKADDR_IN lpstToAddr)
    {
    static ECHOREQUEST echoReq;
    static nId = 1;
    static nSeq = 1;
    int nRet; //填充请求
    echoReq.icmpHdr.Type = ICMP_ECHOREQ;
    echoReq.icmpHdr.Code = 0;
    echoReq.icmpHdr.Checksum = 0;
    echoReq.icmpHdr.ID = nId++;
    echoReq.icmpHdr.Seq = nSeq++; // 填充测试用的数据
    for (nRet = 0; nRet < REQ_DATASIZE; nRet++)
    echoReq.cData[nRet] = ' '+nRet; // Save tick count when sent
    echoReq.dwTime = GetTickCount(); // 把数据放入包中并接受检查
    echoReq.icmpHdr.Checksum = in_cksum((u_short *)&echoReq, sizeof(ECHOREQUEST)); //发送请求的显示信息     
    nRet = sendto(s, /* socket */
     (LPSTR)&echoReq, /* 缓冲 */
     sizeof(ECHOREQUEST),
     0, /* 标志 */
     (LPSOCKADDR)lpstToAddr, /* 定义 */
     sizeof(SOCKADDR_IN));   /* 地址长度 */
    return (nRet);
    }DWORD CUDPPing::RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)
    {
    ECHOREPLY echoReply;
    int nRet;
    int nAddrLen = sizeof(struct sockaddr_in); // 刷新请求连接的信息
    nRet = recvfrom(s, // socket
    (LPSTR)&echoReply, // 缓冲
    sizeof(ECHOREPLY), // 缓冲大小
    0, // 标志
    (LPSOCKADDR)lpsaFrom, // 源地址
    &nAddrLen); // 指向地址长度的指针 // 检测返回的信息 // 返回 time sent and IP TTL
    *pTTL = echoReply.ipHdr.TTL; return(echoReply.echoRequest.dwTime);    }u_short CUDPPing::in_cksum(u_short *addr, int len)
    { register int nleft = len;
    register u_short *w = addr;
    register u_short answer;
    register int sum = 0; /*
     *  由于十进制比较简单采用32位
     *  采用可延伸的16位进制。
     *  当需要时从高16位可扩展至低16位.
     */
    while( nleft > 1 )  
    {
    sum += *w++;
    nleft -= 2;
    } /* 如果有必要。提出一字节 */
    if( nleft == 1 ) {
    u_short u = 0; *(u_char *)(&u) = *(u_char *)w ;
    sum += u;
    } /*
     * 从高16位到低16位
     */
    sum = (sum >> 16) + (sum & 0xffff); /* 添加高16位到低16位 */
    sum += (sum >> 16);
    answer = ~sum; /* 截至16位 */
    return (answer);
    }
      

  5.   

    我的Send是成功的,且send后我这边没有立即关闭连接,但我知道对方收到数据并发回响应数据后立即发关闭了连接,只是我这边没关闭而已,不知道是不是这个问题在影响。
    如果是,怎么解决?
    Ping代码对我没用,要是Ping的话,用个IP Helper 函数一个调用就完事了。
      

  6.   

    对方:
    Call WSAAsyncSelect to register for FD_CLOSE notification. 
    Call shutdown with how=SD_SEND. 
    When FD_CLOSE received, Call closesocket. 
    本方:
    Call WSAAsyncSelect to register for FD_CLOSE notification. 
    Call shutdown with how=SD_SEND. 
    When FD_CLOSE received, call recv until zero returned, or SOCKET_ERROR. 
    Call closesocket.
      

  7.   

    呵呵,问题就是,对方程序我是没法改的,是远程HTTP服务器
      

  8.   

    试一下I/O Completion Ports
    CreateIoCompletionPort(.....)
      

  9.   

    不是吧,用I/O Completion Ports的话,我的程序得动大手术了,一大堆的代码都得重写了
      

  10.   

    想提醒你一下,recv,比如你想收,1024个字节,但是有时会只会收到小于1024的字节,我想你应该判断一下你收的够不够。可能是这个问题吧。
      

  11.   

    就是没有收够啊, 我给16K的缓冲区,也只能收4K多一些,每次收到的数据好像都一样长,只是不够,这个时候应该可以再次调用recv来接收剩下部分的,可是一调用就出错了!
      

  12.   

    在我的记忆中10038错误,只有可能是对方或者自己关闭了套节字才会出现。
    建议你相信GetLastError()给出的错误代码和msdn的解释
    这样的话,如果不是由于你自己关闭了socket,那么就是服务器方已经关闭了。
    另外建议客户端使用异步,同时在SOCKET上等待FD_READ和FD_CLOSE消息,这样,应该可以确认是否对方关闭了套节字造成了错误。如果确实等到了FD_READ消息而出现10038错误,那么大概就是在数据传输过程中,对方关闭了套节字。
    (小弟菜鸟,胡乱说两句,不对也别见怪哈^_^)
      

  13.   

    我没有发送程序,我是连接到一个网站来测试的,那个网站返回的HTML页面数据大于16K,我建立了一个16K的缓冲区,但在接收循环中的第一次时只能收到 8712 Bytes 的数据,第二次循环时就出错了。
    很奇怪,这个收到的数据长度总是这个值!我试着用 telnet www.???.com  80连接进去
    然后发送 GET /index.htm HTTP/1.0
             Host: www.???.com过去,telnet得到的数据就是全部index.htm页面,而我的程序就不行 :(
    我要给这个另外加分了,看来100分不够,再加100分,另开贴给分。
      

  14.   

    我又晕!~~~~~~~~~~~~~~~刚试把把接收部分改为 异步接收 ,结果一样,我操!下面是我改为异步接收后的代码片断:
    ====================================================================
    HANDLE hEvent = WSACreateEvent();
    if (hEvent != WSA_INVALID_EVENT)
    {
    if (WSAEventSelect(sckHTTPSocket, hEvent,  FD_READ) == 0 &&
    WSAWaitForMultipleEvents(1, &hEvent, FALSE, m_nRecvTimeout, FALSE) != WAIT_OBJECT_0)
    {
    free(pBuffer);
    return NULL;
    }
    CloseHandle(hEvent);
    }WSABUF buf;
    buf.buf = pBuffer+iRecvSize;
    buf.len = iBufferSize - iRecvSize;
    DWORD curSize;
    DWORD flags = 0;
    int iErr = WSARecv(sckHTTPSocket, &buf, 1, &curSize, &flags, NULL, NULL);
    int i = WSAGetLastError(); // 第二次执行到这里时,iErr = -1 , i = 10038
      

  15.   

    我又晕!~~~~~~~~~~~~~~~刚试把把接收部分改为 异步接收 ,结果一样,我操!下面是我改为异步接收后的代码片断:
    ====================================================================
    HANDLE hEvent = WSACreateEvent();
    if (hEvent != WSA_INVALID_EVENT)
    {
    if (WSAEventSelect(sckHTTPSocket, hEvent,  FD_READ) == 0 &&
    WSAWaitForMultipleEvents(1, &hEvent, FALSE, m_nRecvTimeout, FALSE) != WAIT_OBJECT_0)
    {
    free(pBuffer);
    return NULL;
    }
    CloseHandle(hEvent);
    }WSABUF buf;
    buf.buf = pBuffer+iRecvSize;
    buf.len = iBufferSize - iRecvSize;
    DWORD curSize;
    DWORD flags = 0;
    int iErr = WSARecv(sckHTTPSocket, &buf, 1, &curSize, &flags, NULL, NULL);
    int i = WSAGetLastError(); // 第二次执行到这里时,iErr = -1 , i = 10038
      

  16.   

    是不是你第一次已经全部收到了?
    是不是用了strcpy,它只是将数据拷贝到buffer出现'/0'为止?
    好像采用异步I/O,数据到了接收端缓冲区,就会自动读取,不需要循环来接收
    debug,看一下buffer收到的数据
      

  17.   

    没有,第一次没有收到全部数据,只收了 8712个字节,全部数据长度大于16K,
    我判断收到数据的长度不是用strcpy之类来判断的,而是依据 recv()的返回值来判断的。
      

  18.   

    另外,我原本是使用blocking mode的,因为出了这个问题,所以改为non-blocking mode,为了确保调用recv时有数据可读,我使用了FD_READ并等待事件,结果还是不行。附:据MSDN说 发生10038(无效socket)错误时,有两种可能:
    1.  socket句柄确实无效
    2.  select 参数有错
    我估计是问题可能是第二个原因,但为什么第一次时是可以的?我程序中用到的select代码(注:前面代码中的select不影响)
    =======================================================struct fd_set fdSet;
    struct timeval tmvTimeout={0L,0L};
       
    FD_ZERO(&fdSet);
    FD_SET(sckHTTPSocket, &fdSet); 
     
    if ( select(0,&fdSet,NULL,NULL,&tmvTimeout)==SOCKET_ERROR)
    {

    }
      

  19.   

    用组塞模式,先不用select了,就是循环调用recv,收一次,累加一次,弹一个MessageBox,看看到底收了多少,
    这个问题不难,我应该可以帮你搞定。
      

  20.   

    fbmsf(FBM) 谢谢你了,搞定了请你吃分数套餐 :) 我试下先
      

  21.   

    这样,你先做一个实验,不用http,收网页的数据,
    而是你另外做一个服务程序,用来发送17k的数据,看用你的代码(recv)可以全部收到不?我想知道到底是因为收的网页的问题还是你的代码的问题?
      

  22.   

    试了,结果还是收不完整,不管第一次接收时buffer是否被填满了
      

  23.   

    应该是你的接收有问题,我们在广域网通过拨vpn测试,发送36K的数据也没有问题,每个包只传输1k的数据。在局域网一次发送36k,接收也没问题。
      

  24.   

    真是怪,我上面给出的代码基本上算是与收发相关的全部代码了,而我这里是局域网通过ADSL上网。
    大家多帮帮忙啊。
      

  25.   

    我估计也许是的你接收缓冲区有问题,
    我写的每次收1k 的,即便是几GB的数据(收发文件夹)也没有过问题。帮你想.........
      

  26.   

    哈哈,很快就成功了,那时你对winsock 的理解又更加深刻了
      

  27.   


    在BinaryTreeEx的帮助下搞定了,谢谢大家,另开贴给分!原因出在ParseResponseHeader()一句上,我的代码在检测服务器返回的HTTP头部内容时,
    发现服务器返回了 Connection: close 一句便立即断开了连接,所以后面的数据没法再接收了。这100分给这里出现了的人另请fbmsf(FBM)   BinaryTreeEx
    到http://expert.csdn.net/Expert/topic/2475/2475669.xml?temp=.499859领分