www.gusucode.com > 一些VC++网络编程实例源代码-源码程序 > 一些VC++网络编程实例源代码-源码程序\code\第八章\Download\HTTPDownload.cpp
//Download by http://www.NewXing.com // HTTPDownload.cpp: implementation of the CHTTPDownload class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Download.h" #include "HTTPDownload.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CHTTPDownload::CHTTPDownload() { for(int i = 0; i < 4; i++){ m_bTerminate[i] = FALSE; } m_bSupportResume = FALSE; m_bResume = FALSE; } CHTTPDownload::~CHTTPDownload() { m_lsTask.RemoveAll(); } BOOL CHTTPDownload::StartTask(CString remoteurl, CString localfile) { if(remoteurl.IsEmpty()) return FALSE; if(!ParseURL(remoteurl)){ remoteurl = _T("http://") + remoteurl; if(!ParseURL(remoteurl)){ TRACE("Requested URL is invalid!\n"); return FALSE; } } m_strSavePath = localfile; m_strSavePath.TrimLeft(); m_strSavePath.TrimRight(); if(m_strSavePath.IsEmpty() ) return FALSE; m_strTempSavePath = m_strSavePath; m_strTempSavePath += ".down"; FILE* fp = NULL; if((fp = fopen(m_strTempSavePath, "r")) == NULL){ m_state.range[0] = -1; m_bResume = FALSE; } else{ m_bResume = TRUE; char* str = new char[1024]; memset(str, 0, 1024); fgets(str, 1024, fp); m_state.url.Empty(); m_state.url += str; m_state.url = m_state.url.Left(m_state.url.GetLength() - 2); memset(str, 0, 1024); fgets(str, 1024, fp); m_state.localfile.Empty(); m_state.localfile += str; m_state.localfile = m_state.localfile.Left(m_state.localfile.GetLength() - 2); delete [] str; fread(&m_state.length, sizeof(LONG), 1, fp); fread(&m_state.time, sizeof(CTime), 1, fp); fread(m_state.range, sizeof(LONG), 8, fp); fclose(fp); } if(SendRequest() != SENDREQUEST_SUCCESS){ TRACE("Remote Web Server is not reachable,or somthing else error occured!\n"); return FALSE; } if(m_state.range[0] == -1){ m_state.localfile = localfile; m_state.url = remoteurl; m_state.time = m_TimeLastModified; m_state.length = m_dwFileSize; for(int i = 0; i < 4; i++){ m_state.range[i * 2] = i * (m_dwFileSize / 4); m_state.range[i * 2 + 1] = (i + 1) * (m_dwFileSize / 4) - 1; } m_state.range[7] = m_dwFileSize - 1; } else{ if(m_state.url != remoteurl){ AfxMessageBox("Maybe the download file is not you want,you can try to save as another file!"); return FALSE; } if(m_state.time < m_TimeLastModified || m_state.range[7] != (LONG)(m_dwFileSize - 1)){ m_state.time = m_TimeLastModified; m_state.length = m_dwFileSize; for(int i = 0; i < 4; i++){ m_state.range[i * 2] = i * (m_dwFileSize / 4); m_state.range[i * 2 + 1] = (i + 1) * (m_dwFileSize / 4); } m_state.range[7] = m_dwFileSize - 1; } } return TRUE; } BOOL CHTTPDownload::ParseURL(CString str) { str.TrimLeft(); if(str.IsEmpty()) return FALSE; CString strURL = str; // 清除数据 m_strServer = _T(""); m_strObject = _T(""); m_nPort = 0; int nPos = strURL.Find("://"); if( nPos == -1 ) return FALSE; // 进一步验证是否为http:// CString strTemp = strURL.Left( nPos+lstrlen("://") ); strTemp.MakeLower(); if( strTemp.Compare("http://") != 0 ) return FALSE; strURL = strURL.Mid( strTemp.GetLength() ); nPos = strURL.Find('/'); if ( nPos == -1 ) return FALSE; m_strObject = strURL.Mid(nPos); strTemp = strURL.Left(nPos); /////////////////////////////////////////////////////////////// /// 注意:并没有考虑URL中有用户名和口令的情形和最后有#的情形 /// 例如:http://abc@def:www.yahoo.com:81/index.html#link1 /// ////////////////////////////////////////////////////////////// // 查找是否有端口号 nPos = strTemp.Find(":"); if( nPos == -1 ) { m_strServer = strTemp; m_nPort = DEFAULT_HTTP_PORT; } else { m_strServer = strTemp.Left( nPos ); strTemp = strTemp.Mid( nPos+1 ); m_nPort = (USHORT)_ttoi((LPCTSTR)strTemp); } return TRUE; } UINT CHTTPDownload::SendRequest(BOOL bHead) { CString strVerb; if( bHead ) strVerb = _T("HEAD "); else strVerb = _T("GET "); CString strSend,strHeader,strRange; int iStatus = 0,nRet; char szReadBuf[1025]; DWORD dwContentLength,dwStatusCode; while (TRUE) { if(m_pSocket.m_hSocket != NULL) m_pSocket.Close(); m_pSocket.Create(); m_pSocket.Connect(m_strServer, m_nPort); strSend = strVerb + m_strObject + " HTTP/1.1\r\n"; strSend += "Host: " + m_strServer + "\r\n"; strSend += "Accept: */*\r\n"; strSend += "Pragma: no-cache\r\n"; strSend += "Cache-Control: no-cache\r\n"; if( !m_strReferer.IsEmpty() ) strSend += "Referer: " + m_strReferer + "\r\n"; strSend += "Connection: close\r\n"; strRange = "Range: bytes=100-\r\n"; strSend += strRange; //必须要加一个空行,否则Http服务器将不会应答 strSend += "\r\n"; int ret = m_pSocket.Send(strSend.GetBuffer(0), strSend.GetLength()); strSend.ReleaseBuffer(); strHeader.Empty(); while( TRUE ) { ZeroMemory(szReadBuf,1025); ret = m_pSocket.Receive(szReadBuf, 1025); if( szReadBuf[0] == '\0' ) // We have encountered "\r\n\r\n" break; strHeader += szReadBuf; if( iStatus == 0) strHeader += "\r\n"; } nRet = GetInfo(strHeader,dwContentLength, dwStatusCode,m_TimeLastModified); switch ( nRet ) { case HTTP_FAIL: return SENDREQUEST_FAIL; break; case HTTP_ERROR: return SENDREQUEST_ERROR; break; case HTTP_REDIRECT: continue; break; case HTTP_OK: m_dwDownloadSize = dwContentLength + 100; // 应该判断一下服务器是否支持断点续传 if( strRange.IsEmpty() ) m_dwFileSize = dwContentLength + 100; // 整个文件的长度 else { if ( dwStatusCode == 206 ) //支持断点续传 { m_bSupportResume = TRUE; m_dwFileSize = dwContentLength + 100; } else //不支持断点续传 { m_bSupportResume = FALSE; m_dwFileSize = dwContentLength + 100; } } return SENDREQUEST_SUCCESS; break; default: return SENDREQUEST_FAIL; break; } } m_pSocket.Close(); return SENDREQUEST_SUCCESS; } UINT CHTTPDownload::GetInfo(LPCTSTR lpszHeader, DWORD &dwContentLength, DWORD &dwStatusCode, CTime &TimeLastModified) { dwContentLength = 0; dwStatusCode = 0; TimeLastModified= CTime::GetCurrentTime(); CString strHeader = lpszHeader; strHeader.MakeLower(); //拆分出HTTP应答的头信息的第一行 int nPos = strHeader.Find("\r\n"); if (nPos == -1) return HTTP_FAIL; CString strFirstLine = strHeader.Left(nPos); // 获得返回码: Status Code strFirstLine.TrimLeft(); strFirstLine.TrimRight(); nPos = strFirstLine.Find(' '); if( nPos == -1 ) return HTTP_FAIL; strFirstLine = strFirstLine.Mid(nPos+1); nPos = strFirstLine.Find(' '); if( nPos == -1 ) return HTTP_FAIL; strFirstLine = strFirstLine.Left(nPos); dwStatusCode = (DWORD)_ttoi((LPCTSTR)strFirstLine); // 检查返回码 if( dwStatusCode >= 300 && dwStatusCode < 400 ) //首先检测一下服务器的应答是否为重定向 { nPos = strHeader.Find("location:"); if (nPos == -1) return HTTP_FAIL; CString strRedirectFileName = strHeader.Mid(nPos + strlen("location:")); nPos = strRedirectFileName.Find("\r\n"); if (nPos == -1) return HTTP_FAIL; strRedirectFileName = strRedirectFileName.Left(nPos); strRedirectFileName.TrimLeft(); strRedirectFileName.TrimRight(); // 设置Referer m_strReferer = m_strDownloadUrl; // 判断是否重定向到其他的服务器 nPos = strRedirectFileName.Find("http://"); if( nPos != -1 ) { m_strDownloadUrl = strRedirectFileName; // 检验要下载的URL是否有效 if ( !ParseURL(m_strDownloadUrl)) return HTTP_FAIL; return HTTP_REDIRECT; } // 重定向到本服务器的其他地方 strRedirectFileName.Replace("\\","/"); // 是相对于根目录 if( strRedirectFileName[0] == '/' ) { m_strObject = strRedirectFileName; return HTTP_REDIRECT; } // 是相对当前目录 int nParentDirCount = 0; nPos = strRedirectFileName.Find("../"); while (nPos != -1) { strRedirectFileName = strRedirectFileName.Mid(nPos+3); nParentDirCount++; nPos = strRedirectFileName.Find("../"); } for (int i=0; i<=nParentDirCount; i++) { nPos = m_strDownloadUrl.ReverseFind('/'); if (nPos != -1) m_strDownloadUrl = m_strDownloadUrl.Left(nPos); } m_strDownloadUrl = m_strDownloadUrl+"/"+strRedirectFileName; if ( !ParseURL(m_strDownloadUrl)) return HTTP_FAIL; return HTTP_REDIRECT; } // 服务器错误,可以重试 if( dwStatusCode >= 500 ) return HTTP_ERROR; // 客户端错误,重试无用 if( dwStatusCode >=400 && dwStatusCode <500 ) return HTTP_FAIL; // 获取ContentLength nPos = strHeader.Find("content-length:"); if (nPos == -1) return HTTP_FAIL; CString strDownFileLen = strHeader.Mid(nPos + strlen("content-length:")); nPos = strDownFileLen.Find("\r\n"); if (nPos == -1) return HTTP_FAIL; strDownFileLen = strDownFileLen.Left(nPos); strDownFileLen.TrimLeft(); strDownFileLen.TrimRight(); // Content-Length: dwContentLength = (DWORD) _ttoi( (LPCTSTR)strDownFileLen ); // 获取Last-Modified: nPos = strHeader.Find("last-modified:"); if (nPos != -1) { CString strTime = strHeader.Mid(nPos + strlen("last-modified:")); nPos = strTime.Find("\r\n"); if (nPos != -1) { strTime = strTime.Left(nPos); strTime.TrimLeft(); strTime.TrimRight(); TimeLastModified = GetTime(strTime); } } return HTTP_OK; } CTime CHTTPDownload::GetTime(LPCTSTR lpszTime) { int nDay,nMonth,nYear,nHour,nMinute,nSecond; CString strTime = lpszTime; int nPos = strTime.Find(','); if (nPos != -1) { strTime = strTime.Mid(nPos+1); strTime.TrimLeft(); CString strDay,strMonth,strYear,strHour,strMinute,strSecond; CString strAllMonth = "jan,feb,mar,apr,may,jan,jul,aug,sep,oct,nov,dec"; strDay = strTime.Left(2); nDay = atoi(strDay); strMonth = strTime.Mid(3,3); strMonth.MakeLower(); nPos = strAllMonth.Find(strMonth); if (nPos != -1) { strMonth.Format("%d",((nPos/4)+1)); nMonth = atoi(strMonth); } else nMonth = 1; strTime = strTime.Mid(6); strTime.TrimLeft(); nPos = strTime.FindOneOf(" \t"); if (nPos != -1) { strYear = strTime.Left(nPos); nYear = atoi(strYear); } else nYear = 2000; strTime = strTime.Mid(nPos+1); strHour = strTime.Left(2); nHour = atoi(strHour); strMinute = strTime.Mid(3,2); nMinute = atoi(strMinute); strSecond = strTime.Mid(6,2); nSecond = atoi(strSecond); } CTime time(nYear,nMonth,nDay,nHour,nMinute,nSecond); return time; } UINT CHTTPDownload::ThreadFunc(int index) { CSocket pSocket; pSocket.Create(); pSocket.Connect(m_strServer, m_nPort); CString strSend, strRange, strHeader; char szReadBuf[1025]; strSend = "GET " + m_strObject + " HTTP/1.1\r\n"; strSend += "Host: " + m_strServer + "\r\n"; strSend += "Accept: */*\r\n"; strSend += "Pragma: no-cache\r\n"; strSend += "Cache-Control: no-cache\r\n"; if( !m_strReferer.IsEmpty() ) strSend += "Referer: " + m_strReferer + "\r\n"; strSend += "Connection: close\r\n"; strRange.Format("Range: bytes=%d-%d\r\n", m_state.range[2 * index], m_state.range[2 * index + 1]); if(m_bSupportResume) strSend += strRange; //必须要加一个空行,否则Http服务器将不会应答 strSend += "\r\n"; int ret = pSocket.Send(strSend.GetBuffer(0), strSend.GetLength()); strSend.ReleaseBuffer(); DWORD lpArgument; if(!pSocket.AsyncSelect(0) && !pSocket.IOCtl(FIONBIO, &lpArgument)) return -1; strHeader.Empty(); int iStatus = 0; ZeroMemory(szReadBuf,1025); ret = pSocket.Receive(szReadBuf, 1025); int n = GetHeadLength(szReadBuf); CFile file; CString name; name.Format("%d", index); name = m_strTempSavePath + name; int bOpen = 0; if(!m_bResume) bOpen = file.Open(name, CFile::modeCreate | CFile::modeWrite); else bOpen = file.Open(name, CFile::modeWrite); if(bOpen == 0){ TRACE("Error in file open!\n"); return -10; } file.SeekToEnd(); TRACE("Entering the key section!\n"); file.Write(szReadBuf + n, ret - n); int sum = ret - n, num = 0; while(1){ if(m_bTerminate[index]){ m_state.range[2 * index] = m_state.range[2 * index] + sum; return -5; } ZeroMemory(szReadBuf,1025); if(!(num = pSocket.Receive(szReadBuf, 1025)) || num == SOCKET_ERROR) break; // (EOF||network error) else{ file.Write(szReadBuf, num); sum += num; } } file.Close(); return 0; } int CHTTPDownload::GetHeadLength(char *lpData) { int ndx = 0; CString str; while(1){ str.Empty(); str = GetLine(lpData, ndx); if(str.IsEmpty()) break; } return (ndx); } CString CHTTPDownload::GetLine(char *lpData, int& ndx) { BOOL bLine = FALSE; CString str; while ( bLine == FALSE && ndx < 1025 ) { char ch = (char)(lpData[ndx]); switch( ch ) { case '\r': // ignore break; case '\n': // end-of-line bLine = TRUE; break; default: // other.... str += ch; break; } ++ndx; } return str; }