http下载
Posted yanghongche
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了http下载相关的知识,希望对你有一定的参考价值。
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下载的主要内容,如果未能解决你的问题,请参考以下文章