1、为了支持断点,下载一个文件(假设Server不支持keep-alive)需要首先发请求得到Server上该文件的大小,并发出下载文件的请求,让server从某位置开始下载(又假设server是支持断点的)即有两次请求.-----不知有没这个必要?
   2、想进行Socket的编程的学习,从CAsyncSocket派生一个类CHttpAsyncSocket,我的代码如下:
      
      //在CHttpAsyncSocket头文件定义一下当前下载状态
      typedef enum { CONNECT_NONE = 0, CONNECT_FILEINFO = 1, RECEIVE_FILEINFO = 2, CONNECT_DOWNLOAD = 3, RECEIVE_DOWNLOAD = 4} DOWNLOADSTATUS;
      DOWNLOADSTATUS m_dsCurStatus;      //在CHttpAsyncSocket类实现代码      //连接server
      void CHttpAsyncSocket::BeginDownload()
      {
          //连接Server
         if(Connect(m_strServerIP, m_nServerPort) == SOCKET_ERROR)
          {
      Close();              m_dsCurStatus = CONNECT_NONE;
          } 
          else
          {
              m_dsCurStatus = CONNECT_FILEINFO;
          }
      }      //连接成功,则发送得到文件大小或下载文件请求
      void CHttpAsyncSocket::OnConnect(int nErrorCode)
      {
  CAsyncSocket::OnConnect(nErrorCode);
  if (nErrorCode)
  {
        Close();
              m_dsCurStatus = CONNECT_NONE;
  }
  else if(m_dsCurStatus == CONNECT_FILEINFO)  //请求得到Server上的文件大小、是否支持断点续传等
  {          
         CString strSend;
              strSend.Format( "GET %s HTTP/1.1\r\n"
           "Host: %s:%d\r\n"
      "Accept: */*\r\n"
      "Pragma: no-cache\r\n" 
      "Cache-Control: no-cache\r\n"
      "Connection: close\r\n"
      "Range: bytes=100-\r\n"
      "\r\n",
      m_strFileUrl, m_strServerIP, m_nServerPort);       int ret = Send(strSend.GetBuffer(0), strSend.GetLength());
             if(ret == SOCKET_ERROR)
      {
  Close();                  m_dsCurStatus = CONNECT_NONE;     return;
      }
      else
      {
                  m_dsCurStatus = RECEIVE_FILEINFO;
      }
  }
  else if(m_dsCurStatus == CONNECT_DOWNLOAD)      //请求server从断点处下载文件
  {        
      CString strSend;
              strSend.Format( "GET %s HTTP/1.1\r\n"
              "Host: %s:%d\r\n"
         "Accept: */*\r\n"
      "Pragma: no-cache\r\n" 
      "Cache-Control: no-cache\r\n"
      "Connection: close\r\n"
      "Range: bytes=%d-%d\r\n"
      "\r\n",
      m_strFileUrl, m_strServerIP, m_nServerPort, m_dwLocalFileSize, m_dwServerFileSize);
                              //m_dwLocalFileSize, m_dwServerFileSize分别为该文件本地大小和server上的大小 int ret = Send(strSend.GetBuffer(0), strSend.GetLength()); if(ret == SOCKET_ERROR)
{
      Close();                    m_dsCurStatus = CONNECT_NONE;
}
else
{
                    m_dsCurStatus = RECEIVE_DOWNLOAD;
    m_bAlreadyGetHeader = FALSE;  //http包头解出来标志
}
    }
    else
    {
ASSERT(FALSE);
    }
        }        //接收数据,
        const int MAX_RECV_BUFFER = 1024;        void CHttpAsyncSocket::OnReceive(int nErrorCode)
        {
    CAsyncSocket::OnReceive(nErrorCode);       char szReadBuf[MAX_RECV_BUFFER];
    ZeroMemory(szReadBuf, MAX_RECV_BUFFER);     if (nErrorCode)
    {
Close();
                m_dsCurStatus = CONNECT_NONE;
    } 

    if(m_dsCurStatus == RECEIVE_FILEINFO)   //得到文件大小、是否支持断点等信息
    {
m_dwServerFileSize = 0;
                BOOL  bSupportResume = FALSE;                if(ParseHttpHeader(&m_dwServerFileSize, &bSupportResume) == TRUE) //解析返回来得http包头
{
    Close();  //关闭以前建立的socket连接,问,能否再继续发下载请求?会不会乱?
    Create(); //创建新的socket

      //连接Server
    if(Connect(m_strServerIP, m_nServerPort) == SOCKET_ERROR)
    {
Close(); m_dsCurStatus = CONNECT_NONE;
    }
    else
    {
m_dsCurStatus = CONNECT_DOWNLOAD;
    }
}
else
{
    Close();        m_dsCurStatus = CONNECT_NONE;
}
    }
            else if(m_dsCurStatus == RECEIVE_DOWNLOAD)
    {
                //打开本地文件,准备接受数据
CFile file;
int bOpenSuc = file.Open(m_strLocalFile, CFile::modeCreate | CFile::modeWrite | CFile::modeNoTruncate | CFile::typeBinary | CFile::shareExclusive); if(bOpenSuc == FALSE)
{
    TRACE("Error in file open!\n");
                    Close();        m_dsCurStatus = CONNECT_NONE;
}
file.SeekToEnd();         int  ret = 0, nHeadLength = 0;                if(m_bAlreadyGetHeader == FALSE)
{
                    //去掉包头,达到包体
    do
    {
ret = Receive(szReadBuf, MAX_RECV_BUFFER);
nHeadLength = GetHeadLength(szReadBuf);
    }while(ret <= nHeadLength);           
                   
    if(ret > nHeadLength)
    {    
file.Write(szReadBuf + nHeadLength, ret - nHeadLength);  
    }

    m_bAlreadyGetHeader = TRUE; //由于是异步调用OnReceive,所以后面就不用再去掉包头了
} while(TRUE)
{
    ZeroMemory(szReadBuf,MAX_RECV_BUFFER);

    int num = Receive(szReadBuf, MAX_RECV_BUFFER);     if(num == 0 || num == SOCKET_ERROR)  //下载结束或网络错误
    {
if (GetLastError() != WSAEWOULDBLOCK && num == 0)   
{
    file.Close();       //注,下载文件几十K或几百K,老会进入这个!就在也没有响应了!不知为什么!!!
    return;             //难道要用定时器再连接发请求?有方法控制一下吗?
}
       
break; 
    }     file.Write(szReadBuf, num);
}         file.Close();
             }
}
else
{
     ASSERT(FALSE);
}
    }贴了很多代码,不好意思,我刚学socket网络编程,哪位指导一下我这个迷途小程序员....,给个用CAsyncSocket编http下载的程序也行的(是支持断点的就更好了)

解决方案 »

  1.   

    这里有个下载的源码,不知可有用。
    http://www.yangning.com/pages/chuansheng1.1.htm
      

  2.   

    我买的一本书带的光盘有例子,需要请email:[email protected] 我可以发给你。
      

  3.   

    谢谢楼上各位!
    继续请教:如果发起第二个连接,那在CHttpAsyncSocket::OnReceive里调用Receive接收到的数据是第一个连接导致的还是第二个连接导致的?如何判断呢?或者发第二连接的时候第一个连接就自动Stop了?
      

  4.   

    第二个发送的请求是
       "GET http://expert.csdn.net/XXX.zip HTTP/1.1\r\n"
       "Host: n1.n2.n3.n4:port\r\n"
       "Accept: */*\r\n"
       "Pragma: no-cache\r\n" 
       "Cache-Control: no-cache\r\n"
       "Connection: close\r\n"
       "Range: bytes=%nBegin-%nEnd\r\n"
       "\r\n",  
       他要求得到文件从nBegin开始的数据,但第一个请求(我用的方法是跟上面差不多的请求包)也可能会再触发OnReceive响应啊,有什么方法可以区分呢?估计第一条请求可以不用GET,而用其他的请求动作如HEAD来得到(这是HTTP协议本身的问题了)
    另外,OnRecvive接收到的数据(相应上面的第二条请求导致的),接收了一部分,一般是几十 到 几百 K就会停止,即文件还没下载完就再也不会有后续OnReceive的动作,而我用CSocket(阻塞)的话都可以下载完全,不知是否有重联的必要或者我的处理还缺少什么设置?
      

  5.   

    http://www.yangning.com/pages/chuansheng1.1.htm
      

  6.   

    谢了!我现在明白你的意思了:)不过我是在一个CAsyncSocket(派生类CHttpAsyncSocket)对象的函数内部进行了两次连接,实际上也就是两个socket实例。在OnReceive函数里面调用Receive得到待下载文件的大小、是否支持断点(用http的"HEAD "动作)后,再关闭当前的socket,重新create并且connect一次,请求正式下载文件,这次响应是正确的。经过我试验,一个请求只能对应一个socket,如果前一个connect没有close的话,再发请求不会回调到OnReceive,不知道这是不是http协议本身的限制另外用这种方法我下载一个1.53M的文件,下到1.2M-1.46M就停了,每次测试的结果都不一样,不知道什么原因,或许需要什么设置?
      

  7.   

    奇怪了!我在家里用这个CAsyncSocket程序下载老是下载不完全会差几十K到几百K字节,但用IE或CSocket到可以全部下载下来,不过在公司用CAsyncSocket能下载完全...