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;
}


發佈了12 篇原創文章 · 獲贊 31 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章