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下载的主要内容,如果未能解决你的问题,请参考以下文章

Qt多线程http下载器之一:仿百度网盘的http下载器

Http和FTP下载文件

ftp 和http 区别FTP地址与HTTP地址的区别FTP下载和HTTP下载的区别之一就在与此。

HTTP 文件下载:监控下载进度

Http下载文件的登录验证

HTTP/2 和文件下载