int httpdownload(const char* url, const char* localfile)
{
const char* pSplitStr = NULL;
const char* pTemp = NULL;
const char* pLastPos = NULL;
const char* pEndPos = NULL;
int iTempRet = 0;
const int iTempBufSize = 1024;
const int iUrlBufSize = 2084;
const int iRecvBufSize = 64*1024;
const int iHttpHeaderBufSize = 16*1024;
bool bUseBreakMode = false; // 是否啓用斷點續傳
char* pHost = NULL;
char* pRefer = NULL;
int iHostPort = 80;
char* pFileUrl = NULL;
ULONG ulNetIp = 0;
struct hostent* lpHostent = NULL;
SOCKET scSocket = INVALID_SOCKET;
sockaddr_in siSockaddr;
char* pHttpRequest = NULL;
char* pHttpResponse = NULL;
LPBYTE lpBufTemp = (LPBYTE)NULL;
DWORD dwDataLen = 0;
char* pTempBuf = NULL;
char* pHeaderEndPos = NULL;
DWORD dwSentTotal = 0;
bool bRet = true;
int iRecv = 0;
int iRecvedTotal = 0;
int iRetCode = 0;
long lFileOffset = 0;
int iTotalDataSize = 0;
int iRecvedFileBytes = 0;
FILE* fpTempFile = NULL;
FILE* fpBreakFile = NULL;
char* pBreakFileUrl = NULL;
char* pTempFileUrl = NULL;
int i = 0;
WSADATA wdData;
//
if ((strlen(url) == 0) || (strlen(url) > 2083) || (url == NULL))
{
return 1; // 無效的參數
}
if ((strlen(localfile) == 0) || (localfile == NULL))
{
return 1; // 無效的參數
}
//
iTempRet = WSAStartup(0x0202, &wdData);
if (iTempRet != 0)
{
return 2;
}
//
pHost = (char*)malloc(iUrlBufSize);
pRefer = (char*)malloc(iUrlBufSize);
pFileUrl = (char*)malloc(iUrlBufSize);
pHttpRequest = (char*)malloc(iHttpHeaderBufSize);
pHttpResponse = (char*)malloc(iRecvBufSize);
pTempBuf = (char*)malloc(iTempBufSize);
pBreakFileUrl = (char*)malloc(iTempBufSize);
pTempFileUrl = (char*)malloc(iTempBufSize);
//
__try
{
if (pHost == NULL || pRefer == NULL || pFileUrl == NULL || pHttpRequest == NULL || pHttpResponse == NULL || pTempBuf == NULL || pBreakFileUrl == NULL || pTempFileUrl == NULL)
{
return 3;
}
memset(pHost, 0, iUrlBufSize);
memset(pRefer, 0, iUrlBufSize);
memset(pFileUrl, 0, iUrlBufSize);
memset(pHttpRequest, 0, iHttpHeaderBufSize);
memset(pHttpResponse, 0, iRecvBufSize);
memset(pTempBuf, 0, iTempBufSize);
memset(pBreakFileUrl, 0, iTempBufSize);
tagDown:
// 分割URL,http://www.aaa.com/aaa/aaa或者http://www.aaa.com/aaa/aaa?test
pSplitStr = "http://";
iTempRet = strnicmp(url, pSplitStr, strlen(pSplitStr));
if (iTempRet != 0)
{
return 4; // 無效的地址
}
// 得到主機名
pTemp = url + strlen(pSplitStr);
// 查找主機結束符
pLastPos = strchr(pTemp, '/');
if (pLastPos == NULL)
{
strcpy(pHost, pTemp);
pFileUrl[0] = '/';
}
else
{
strncpy(pHost, pTemp, pLastPos-pTemp);
strcpy(pFileUrl, pLastPos);
}
// 查找端口
pLastPos = strchr(pHost, ':');
if (pLastPos == NULL)
{
iHostPort = 80;
}
else
{
iHostPort = atoi(pLastPos+1);
//*pLastPos = '\0';
}
// 解析主機
lpHostent = gethostbyname(pHost);
if (lpHostent == NULL)
{
return 4;
}
if (lpHostent->h_length < 1)
{
return 4;
}
//
ulNetIp = *(ULONG*)(lpHostent->h_addr_list[0]);
// 連接主機
scSocket = socket(AF_INET, SOCK_STREAM, 0);
if (scSocket == INVALID_SOCKET)
{
return 5;
}
siSockaddr.sin_family = AF_INET;
siSockaddr.sin_addr.S_un.S_addr = ulNetIp;
siSockaddr.sin_port = htons(iHostPort);
//
for (i=0; ;)
{
iTempRet = connect(scSocket, (const struct sockaddr*)&siSockaddr, sizeof(siSockaddr));
if (iTempRet != 0)
{
if (++i < 10)
{
Sleep(1000);
continue;
}
return 6;
}
break;
}
// 構建HTTP頭
#define FORMAT_HTTP_GET_REQUEST "GET %s HTTP/1.1\r\n"\
"Accept: */*\r\n"\
"Accept-Language: zh-CN\r\n"\
"User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2)\r\n"\
"Host: %s\r\n"\
"Cache-Control: no-cache\r\n"\
"Expires: 0\r\n"\
"Connection: Keep-Alive\r\n"\
"Referer: %s\r\n"\
"Range: bytes=%d-\r\n"\
"\r\n"
//
sprintf(pHttpRequest, FORMAT_HTTP_GET_REQUEST, pFileUrl, pHost, pRefer, lFileOffset);
// 發送請求
lpBufTemp = (LPBYTE)pHttpRequest;
dwDataLen = strlen(pHttpRequest);
dwSentTotal = 0;
bRet = true;
while (dwSentTotal < dwDataLen)
{
int iSent = send(scSocket, (const char*)(lpBufTemp+dwSentTotal), dwDataLen-dwSentTotal, 0);
if (iSent == 0)
{
bRet = false;
break;
}
else if (iSent == SOCKET_ERROR)
{
bRet = false;
break;
}
else
{
dwSentTotal += iSent;
}
}
// 接收HTTP返回數據
iRecvedTotal = 0;
while (true)
{
iRecv = recv(scSocket, pHttpResponse+iRecvedTotal, iRecvBufSize-iRecvedTotal, 0);
if (iRecv == 0 || iRecv == SOCKET_ERROR)
{
return 7;
}
iRecvedTotal += iRecv;
// 連續找兩個\r\n表示結束
pSplitStr = "\r\n\r\n";
pHeaderEndPos = strstr(pHttpResponse, pSplitStr);
if (pHeaderEndPos != NULL)
{
pHeaderEndPos += strlen(pSplitStr);
break;
}
if (iRecvBufSize == iRecvedTotal)
{
return 8;
}
}
// 解析返回
pLastPos = strchr(pHttpResponse, ' ');
if (pLastPos == NULL)
{
return 9;
}
pLastPos += 1;
pEndPos = strstr(pLastPos, "\r\n");
strncpy(pTempBuf, pLastPos, pEndPos-pLastPos);
pLastPos = strchr(pTempBuf, ' ');
if (pLastPos != NULL)
{
pTempBuf[pLastPos-pTempBuf] = '\0';
}
iRetCode = atoi(pTempBuf);
//
switch (iRetCode)
{
case 301:
case 302:
{
//
const char* pLocationStr = "Location: ";
// 得到重定向後的地址
pLastPos = strstr(pHttpResponse, pLocationStr);
if (pLastPos == NULL)
{
return 8; // 無法獲取到重定向後的地址
}
pLastPos += strlen(pLocationStr);
//
pEndPos = strstr(pLastPos, "\r\n");
//
strcpy(pRefer, pHost);
//
memset(pHost, 0, iUrlBufSize);
strncpy(pHost, pLastPos, pEndPos-pLastPos);
//
closesocket(scSocket);
scSocket = INVALID_SOCKET;
//
goto tagDown;
}
break;
case 200:
case 206:
{
// 得到數據的大小
const char* pContentLength = "Content-Length: ";
const char* pEndPos = NULL;
int iHeaderLen = 0;
int iDataLen = 0;
bool bChunkMode = false;
pLastPos = strstr(pHttpResponse, "Content-Range: ");
if (pLastPos == NULL)
{
bUseBreakMode = false;
}
else
{
bUseBreakMode = true;
}
//
strcpy(pTempFileUrl, localfile);
strcat(pTempFileUrl, ".tmp");
//
if (bUseBreakMode)
{
// 讀取斷點位置
strcpy(pBreakFileUrl, localfile);
strcat(pBreakFileUrl, "_brk");
fpBreakFile = fopen(pBreakFileUrl, "rb+");
if (!fpBreakFile)
{
//
fpBreakFile = fopen(pBreakFileUrl, "wb+");
//
remove(pTempFileUrl);
// 用寫入的模式打開文件
fpTempFile = fopen(pTempFileUrl, "wb+");
}
else
{
// 打開臨時文件
fpTempFile = fopen(pTempFileUrl,"rb+");
if (!fpTempFile)
{
// 臨時文件打開失敗
fpTempFile = fopen(pTempFileUrl, "wb+");
if (!fpTempFile)
{
return 4;
}
//
lFileOffset = 0;
}
else
{
fscanf(fpBreakFile,"%d" , &lFileOffset);
fseek(fpBreakFile, lFileOffset, SEEK_SET);
}
}
}
//
pLastPos = strstr(pHttpResponse, pContentLength);
if (pLastPos == NULL)
{
// 判斷是否爲分塊模式
const char* pTransferEncoding = "Transfer-Encoding: ";
const char* pChunkStr = "chunked";
pLastPos = strstr(pHttpResponse, pContentLength);
if (pLastPos == NULL)
{
return 8;
}
pLastPos += strlen(pTransferEncoding);
if (strnicmp(pLastPos, pChunkStr, strlen(pChunkStr)) != 0)
{
return 9;
}
bChunkMode = true;
return 12;
}
else
{
pLastPos += strlen(pContentLength);
// 解析返回
pSplitStr = "\r\n";
pEndPos = strstr(pLastPos, "\r\n");
strncpy(pTempBuf, pLastPos, pEndPos-pLastPos);
pTempBuf[pEndPos-pLastPos] = '\0';
iTotalDataSize = atoi(pTempBuf);
}
//
iHeaderLen = (pHeaderEndPos - pHttpResponse);
iDataLen = iRecvedTotal - iHeaderLen;
if (iDataLen > 0)
{
if (bChunkMode)
{
return 12;
}
else
{
size_t nSize = 0;
iRecvedFileBytes += iDataLen;
lFileOffset += iDataLen;
// 將數據寫入文件
nSize = fwrite(pHeaderEndPos, iDataLen, 1, fpTempFile);
fflush(fpTempFile);
//
if (bUseBreakMode)
{
// 寫斷點信息
rewind(fpBreakFile);
fprintf(fpBreakFile,"%d", lFileOffset);
fflush(fpBreakFile);
}
}
}
//
while (true)
{
// 接收數據
iRecv = recv(scSocket, pHttpResponse, iRecvBufSize, 0);
if (iRecv == 0)
{
return 10;
}
else if (iRecv == SOCKET_ERROR)
{
return 11;
}
else
{
iRecvedTotal += iRecv;
//
if (bChunkMode)
{
return 12;
}
else
{
iRecvedFileBytes += iRecv;
lFileOffset += iRecv;
//
fwrite(pHttpResponse, iRecv, 1, fpTempFile);
fflush(fpTempFile);
//
if (bUseBreakMode)
{
// 寫斷點信息
rewind(fpBreakFile);
fprintf(fpBreakFile,"%d", lFileOffset);
fflush(fpBreakFile);
}
// 寫文件
if (iRecvedFileBytes == iTotalDataSize)
{
int iRet = 0;
//
//
if (bUseBreakMode)
{
fclose(fpBreakFile);
fpBreakFile = NULL;
//
remove(pBreakFileUrl);
}
//
remove(localfile);
//
fclose(fpTempFile);
fpTempFile = NULL;
//
for (i=0;;)
{
iRet = rename(pTempFileUrl, localfile);
if (iRet != 0)
{
if (++i > 10)
{
return 13;
}
Sleep(1000);
continue;
}
break;
}
//
return 0;
}
}
}
}
//
}
break;
default:
return 9;
}
}
__finally
{
// 釋放資源
free(pHost);
free(pRefer);
free(pFileUrl);
free(pHttpRequest);
free(pHttpResponse);
free(pTempBuf);
if (fpBreakFile)
{
fclose(fpBreakFile);
}
if (fpTempFile)
{
fclose(fpTempFile);
}
}
return -1;
}
http下載
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.