C 语言http GET请求 超小纯净下载工具 (暂时只支持http)第二季

第一季简单实现了下载功能:https://blog.csdn.net/trw777/article/details/104459563

本次主要更新断点多线程断点下载功能:

1、域名有几个IP,建几个线程下载,线程可成倍怎加,但有些域名会有8个IP,线程太多。

2、单挑线程20秒收不到数据,重新换IP建联,连续40秒下载速度低,重新换IP建联。

文笔不好还是直接上代码吧

代码组成部分分为:头文件--1.global.h ,源文件--字符转换(2.ANSI_to_UTF8.cpp), 下载功能(3.Download.cpp) 和 主流程main(4.main.cpp)   

头文件

1.  global.h

#pragma once
#include<stdio.h>						//输入输出-文件
#include<iostream>						//C++输入输出-文件
#include<WinSock2.h>					//SOCKET网络连接
#include<process.h>						//多线程头文件
#include<synchapi.h>					//互斥锁头文件,可不添加从Winsock2.h可以连接到
using namespace std;
#pragma comment(lib, "ws2_32.lib")

#define TNUM 1
extern HANDLE hMutex;					//全局互斥锁
extern int errq;						//全局错误码
extern int thread;						//全局线程数
extern WSADATA WsaData;					//全局SCOKET库
extern float MaxSpeed ;					//全局单线程最大速度
extern char MaxIP[16];					//全局最大速度的IP
//保存IP相关信息
//typedef struct sip {
//	char IP[16] = "";
//	ULONGLONG MinTime = 0;
//	float MaxSpeed = 0;
//	int n = 0;
//};

//保存URL信息
typedef struct httpurl {
	char Http[6] = "";					//协议头
	char Host[64] = "";					//域名
	char Directories[256] = "";			//目录
	char Filename[128] = "";			//文件名
	int  IPType = 0;					//IP类型
	int  IPPort = 0;					//端口
	char IP[16][16] = { "" };			//文本IP
	int	 IPnum = 0;						//IP数量
}HTTPURL, * PHTTPURL;

//保存HTTP请求信息
typedef struct resphead {
	char Statusline[32] = "";			//请求行
	long ContentLength = 0;				//文件大小
	long ContentBlock = 0;				//块大小
	char ContentMD5[40] = "";			//文件MD5验证码
	bool Breakpoint = false;			//是否支持断点下载
}RESPHEAD, * PRESPHEAD;

//保存下载缓存文件休息
typedef struct threadtmp {
	int ThreadId = 0;					//线程ID
	char ThreadIP[16] = "";				//是否保存完成
	FILE* ThreadFtmp = nullptr;			//缓存文件
	long ThreadBlock = 0;				//块大小
	long ThreadStart = 0;				//文件开始位置
	long ThreadSize = 0;				//已经下载大小
	long ThreadEnd = 0;					//文件结束位置
	bool DownComplete = false;			//是否下载完成
	bool SaveComplete = false;			//是否保存完成
}THREADTMP, * PTHREADTMP;

//线程信息
typedef struct Param {
	PHTTPURL phu = nullptr;				//URL结构体指针
	PRESPHEAD prh = nullptr;			//Http请求结构体指针
	PTHREADTMP ftmp;					//缓存文件结构体
	int n = 0;							//线程号
}PARAM, * PPARAM;

//多字节转UTF函数
char* ANSIToUTF8(const char* str);

源文件

2.  ANSI_to_UTF8.cpp

#include <stdio.h>
#include <windows.h>
#define BUFF_SIZE 1024
#pragma warning(disable : 4075)
#pragma warning(disable : 4996)
#pragma warning(disable : 6387)

/*多字符转换为宽字符 --- ANSI -to- Unicode*/
wchar_t* ANSIToUnicode(const char* str)
{
    int textlen;
    wchar_t* result;
    textlen = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
    result = (wchar_t*)malloc((textlen + 1) * sizeof(wchar_t));
    if (0 < (result = (wchar_t*)malloc((textlen + 1) * sizeof(wchar_t))))
    {
        memset(result, 0, (textlen + 1) * sizeof(wchar_t));
        MultiByteToWideChar(CP_ACP, 0, str, -1, (LPWSTR)result, textlen);
        return result;
    }
    return 0;
}

/*宽字符转换为多字符 --- Unicode -to- ANSI*/
char* UnicodeToANSI(const wchar_t* str)
{
    char* result;
    int textlen;
    textlen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
    result = (char*)malloc((textlen + 1) * sizeof(char));
    if (0 < result)
    {
        memset(result, 0, sizeof(char) * (textlen + 1));
        WideCharToMultiByte(CP_ACP, 0, str, -1, result, textlen, NULL, NULL);
        return result;
    }
    return 0;
}

/*UTF8转换为宽字符 --- UTF8 -to- Unicode */
wchar_t* UTF8ToUnicode(const char* str)
{
    int textlen;
    wchar_t* result;
    textlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
    result = (wchar_t*)malloc((textlen + 1) * sizeof(wchar_t));
    if (0 < result)
    {
        memset(result, 0, (textlen + 1) * sizeof(wchar_t));
        MultiByteToWideChar(CP_UTF8, 0, str, -1, (LPWSTR)result, textlen);
        return result;
    }
    return 0;
}

/*宽字符转换为UTF8 --- Unicode -to- UTF8 */
char* UnicodeToUTF8(const wchar_t* str)
{
    char* result;
    int textlen;
    textlen = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
    result = (char*)malloc((textlen + 1) * sizeof(char));
    if (0 < result)
    {
        memset(result, 0, sizeof(char) * (textlen + 1));
        WideCharToMultiByte(CP_UTF8, 0, str, -1, result, textlen, NULL, NULL);
        return result;
    }
    return 0;
}

/*多字符转换为UTF8 --- Unicode -to- UTF8 */
char* ANSIToUTF8(const char* str)
{
    return UnicodeToUTF8(ANSIToUnicode(str));
}

/*UTF8转换为多字符 --- UTF8 -to- ANSI */
char* UTF8ToANSI(const char* str)
{
    return UnicodeToANSI(UTF8ToUnicode(str));
}

3.  Download.cpp

#include"global.h"
#include<stdlib.h>
#include<WS2tcpip.h>
#include <windows.h>
#include<direct.h>
#include <time.h>
#define SENDBUFSIZE 1412
#define RECVBUFSIZE 8192
#pragma warning(disable : 4996)

SOCKADDR_IN addr = { 0 };
char ReqHead[512] = "";

//初始化URL结构体
int InitHttpurl(PHTTPURL phu) {
	char Url[256] = "";
	cout << "\n\n请输入下载地址:";
	scanf_s("%[^\n]", Url, 256);
	char sbuf[256] = "";
	//赋值协议
	sscanf_s(Url, "%[^:]", phu->Http, sizeof(phu->Http));
	cout << "Http = " << phu->Http << endl;
	memset(sbuf, 0, 256);
	sprintf_s(sbuf, "%s://%%[^/]", phu->Http);
	/*cout << "buf1 = " << sbuf << endl;*/
	//赋值域名
	sscanf_s(Url, (const char*)sbuf, phu->Host, sizeof(phu->Host));
	cout << "Host = " << phu->Host << endl;
	memset(sbuf, 0, 256);
	const char* sret = strchr(Url, '.');
	sret = strchr(sret, '/');
	//赋值目录
	strcpy_s(phu->Directories, sret);
	cout << "Directories = " << phu->Directories << endl;
	sret = strrchr(Url, '/');
	//赋值文件名
	strcpy_s(phu->Filename, sret + 1);
	cout << "Filename = " << phu->Filename << endl;
	memset(sbuf, 0, 256);
	//复制端口
	for (size_t i = 0; i < strlen(phu->Directories); i++)
	{
		if (phu->Directories[i] < 0 ) {
			char hbuf[5] = "";
			memset(hbuf, 0, 5);
			memcpy_s(hbuf, 2, &phu->Directories[i], 2);
			memcpy_s(hbuf, 4, ANSIToUTF8(hbuf), 4);
			for (size_t i = 0; i < strlen(hbuf); i++)
			{
				sprintf_s(sbuf, "%s%%%hhX", sbuf, hbuf[i]);
			}
			++i;
		}
		else if(phu->Directories[i] == ' ')
		{
			sprintf_s(sbuf, "%s%%%hhX", sbuf, ' ');
		}
		else
		{
			int p = strlen(sbuf);
			sbuf[p] = phu->Directories[i];
			sbuf[p + 1] = 0;
		}
	}
	//sprintf_s(ReqHead, "GET %s HTTP/1.1\r\nConnection: close\r\n", sbuf);
	
	//赋值请求消息头
	sprintf_s(ReqHead, "GET %s HTTP/1.1\r\nConnection: Keep-alive\r\n", sbuf);
	sprintf_s(ReqHead, "%shost: %s\r\n", ReqHead, phu->Host);
	PHOSTENT HostIp;
	char* pchr = nullptr;
	char chost[64] = "";
	if (nullptr == (pchr = strchr(phu->Host, ':')))
	{
		strcpy_s(chost, phu->Host);
		if (!strcmp(phu->Http, "http"))
		{
			phu->IPPort = 80;
		}
		else if (!strcmp(phu->Http, "https"))
		{
			phu->IPPort = 443;
		}
	}
	else
	{
		int port = 0;
		sscanf_s(phu->Host, "%[^:]", chost, sizeof(chost));
		sscanf_s(pchr + 1, "%d", &port);
		phu->IPPort = port;
	}

	//域名转换IP
	HostIp = gethostbyname(chost);
	if (HostIp == NULL)
	{
		cout << "!!域名转换IP失败:[" << WSAGetLastError() << "]!!" << endl;
		Sleep(3000);
		return -102;
	}
	else {
		//地址类型
		phu->IPType = HostIp->h_addrtype;
		//IP地址
		for (int i = 0; i < 16; i++)

		{
			if (HostIp->h_addr_list[i])
			{
				strcpy_s(phu->IP[i], inet_ntoa(*(struct in_addr*)HostIp->h_addr_list[i]));
			}
			else
			{
				phu->IPnum = i;
				break;
			}
		}
		cout << "IP个数为:" << phu->IPnum << endl;
		for (int i = 0; i < phu->IPnum; i++)
		{
			printf_s("IP[%d] = %s\n", i, phu->IP[i]);
		}
		/*cout << "** 域名转换IP成功 **" << endl ;*/
	}
	return 0;
}

//初始化请求结构体
int InitThreadTmp(PRESPHEAD prh, PTHREADTMP ftmp) {
	for (int i = 0; i < thread; i++)
	{
		ftmp[i].ThreadId = i;
		tmpfile_s(&ftmp[i].ThreadFtmp);
		ftmp[i].DownComplete = false;
		ftmp[i].SaveComplete = false;
		/*printf_s("\n\nmain-ftmp%d=%p\n\n",i,FileTmp[i].ThreadFtmp);*/
		ftmp[i].ThreadStart = i * prh->ContentBlock;
		if (thread - 1 > i)
		{
			ftmp[i].ThreadEnd = (i + 1) * prh->ContentBlock - 1;
			ftmp[i].ThreadBlock = prh->ContentBlock;
		}
		else {
			ftmp[i].ThreadEnd = prh->ContentLength - 1;
			ftmp[i].ThreadBlock = prh->ContentLength - ftmp[i].ThreadStart;
		}
	}
	return 0;
}

//初始SOCKET
SOCKET InitSocket(PHTTPURL phu,int h = 1, int n = -1) {
	SOCKET sockid = INVALID_SOCKET;
	int ret = 0;
	addr.sin_port = htons(phu->IPPort);
	addr.sin_addr.S_un.S_addr = inet_addr(phu->IP[h % phu->IPnum]);
	addr.sin_family = AF_INET;
	//cout << "IP:PORT = " << phu->IP[h % phu->IPnum] << ":" << phu->IPPort << endl;

	if (INVALID_SOCKET == (sockid = socket(AF_INET, SOCK_STREAM, 0)))
	{
		cout << "!! 线程[" << n << "]创建socket失败:[" << WSAGetLastError() << "] !!" << endl;
		closesocket(sockid);
		sockid = NULL;
		return INVALID_SOCKET;
	}
	else {
		//cout << "** 线程[" << n << "]创建socket成功 -- server_sockid:[" << sockid << "] **" << endl;
	}
	u_long nl = 1;
	int err = ioctlsocket(sockid, FIONBIO, &nl);						//设定SOCKET为非阻塞状态
	if (SOCKET_ERROR == err) {
		cout << "!! 设定socket非阻塞失败:WSAGetLastError["<< WSAGetLastError()<< "]  !!" << endl;
		Sleep(3000);
		exit(1);
	}
	else {
		//cout << "** 设定socket非阻塞成功 **" << endl;
	}

	while (true)
	{
		ret = connect(sockid, (SOCKADDR*)&addr, sizeof(addr));		//连接到某一个具体的服务器 
		if (ret == INVALID_SOCKET)
		{
			int errcode = WSAGetLastError();
			/*cout << "WSAGetLastError() = " << errcode << endl;*/
			if (errcode == WSAEWOULDBLOCK || errcode == WSAEINVAL|| errcode == WSAEALREADY)  //表示服务器端未准备好,继续循环  
			{
				Sleep(100);
				continue;
			}
			else
			{
				if (errcode == WSAEISCONN) //连接成功,则退出  
				{
					cout << "** 线程[" << n << "]链接服务器成功 IP = " << phu->IP[h % phu->IPnum] << " **" << endl << endl;
					break;
				}
				else                      //否则连接失败,关闭客户端套接字并释放套接字库  
				{
					printf("connect failed!");
					closesocket(sockid);
					cout << "!! 线程[" << n << "]链接服务器失败:[" << WSAGetLastError() << "] !!" << endl;
					return INVALID_SOCKET;
				}
			}
		}

	}
	
	return sockid;
}

//初始化请求消息
int InitRespHead(PRESPHEAD prh, PHTTPURL phu) {
	SOCKET sockid = INVALID_SOCKET;
	FILE* ftmp = nullptr;
	int ret = 0, err = 0;
	int h = 0;
	ULONGLONG SecondsStart = GetTickCount64();		//开始时间
	ULONGLONG SecondsNow = GetTickCount64();		//现在时间
	ULONGLONG SecondsLast = GetTickCount64();		//结束时间
CreatS:
	SecondsNow = GetTickCount64();
	char* SendBuf = new char[SENDBUFSIZE];
	memset(SendBuf, 0, SENDBUFSIZE);
	//发送断点下载请求
	for (int i = 0; i < phu->IPnum; i++)
	{
		if (INVALID_SOCKET != (sockid = InitSocket(phu, i+h))) {
			
			break;
		}
		else
		{
			err = WSAGetLastError();
			cout << "WSAGetLastError() = " << err << endl;
			if (i + 1 >= phu->IPnum) {
				cout << " ! ! ! 初始化“RespHead”时连接所有服务器失败 ! ! !" << endl;
				return -108;
			}
		}
	}
	memset(SendBuf, 0, SENDBUFSIZE);
	sprintf_s(SendBuf, SENDBUFSIZE, "%sRange: bytes=-1\r\n\r\n", ReqHead);
	/*cout << "----------------SendData2-----------------------\n" << SendBuf << "----------------end---------------------- " << endl;*/
	ret = send(sockid, SendBuf, strlen(SendBuf), 0);
	if (SOCKET_ERROR != ret) {
		cout << "ReqHead 数据发送成功" << endl;
	}
	else {
		err = WSAGetLastError();
		cout << "WSAGetLastError() = " << err << endl;
		cout << "ReqHead 数据发送失败" << endl;
		return -202;
	}
	memset(SendBuf, 0, SENDBUFSIZE);
	delete[] SendBuf;
	SendBuf = nullptr;

	//接收断点请求应答,获取是否支持断点下载
	char* RecvBuf = new char[SENDBUFSIZE];
	memset(RecvBuf, 0, SENDBUFSIZE);
	while (SOCKET_ERROR == (ret = recv(sockid, RecvBuf, SENDBUFSIZE, 0)))
	{
		/*err = WSAGetLastError();
		cout << "WSAGetLastError() = " << err << endl;*/
		if (GetTickCount64() - SecondsNow >= 10000) {
			closesocket(sockid);
			sockid = NULL;
			++h;
			Sleep(2000);
			goto CreatS;
		}

		Sleep(60);
	}
	/*cout << "----------------RecvData2-----------------------\n" << RecvBuf << endl << "----------------end---------------------- " << endl;*/
	tmpfile_s(&ftmp);
	fwrite(RecvBuf, 1, ret, ftmp);
	rewind(ftmp);
	fscanf_s(ftmp, "%[^\r]", prh->Statusline, sizeof(prh->Statusline));
	if (!strncmp(prh->Statusline, "HTTP/1.1 206", 12)) {
		cout << "服务器持断点下载:" << prh->Statusline << endl;
		prh->Breakpoint = true;
	}
	else
	{
		cout << "服务器不支持断点下载:" << prh->Statusline << endl;
		prh->Breakpoint = false;
	}
	char  sbuf[128];
	memset(sbuf, 0, 128);
	while (0 == strcmp(prh->ContentMD5, "") || 0 == prh->ContentLength)
	{
		memset(sbuf, 0, 128);
		fscanf_s(ftmp, " %[^:\r]%*c", sbuf, 128);
		sbuf[strlen(sbuf)] = 0;
		if (!strcmp(sbuf, "Content-Range")) {
			while (fgetc(ftmp) != '/');
			fscanf_s(ftmp, "%d", &prh->ContentLength);
			prh->ContentBlock = prh->ContentLength / thread;
			cout << "文件总大小为:" << prh->ContentLength << endl;
		}
		else if (!strcmp(sbuf, "Content-MD5")) {
			fscanf_s(ftmp, "%s", prh->ContentMD5, sizeof(prh->ContentMD5));
			cout << "文件MD5为:" << prh->ContentMD5 << endl;
		}
		else if (ret <= ftell(ftmp))
			break;
	}
	memset(RecvBuf, 0, SENDBUFSIZE);
	delete[] RecvBuf;
	RecvBuf = nullptr;
	fclose(ftmp);
	ftmp = nullptr;
	closesocket(sockid);
	sockid = NULL;
	Sleep(1000);
	return 0;
}

//普通下载
int Download(PRESPHEAD prh, PHTTPURL phu) {
	SOCKET sockid = INVALID_SOCKET;
	char* SendBuf = new char[SENDBUFSIZE];
	memset(SendBuf, 0, SENDBUFSIZE);
	char sbuf[128];
	memset(sbuf, 0, 128);
	FILE* DFile;
	int ret = 0, err = 0;
	long Fsize1 = 0, Fsize2 = 0, Ltime1 = 0, Ltime2 = 0;
	for (int i = 0; i < phu->IPnum; i++)
	{
		if (0 >= (sockid = InitSocket(phu, i))) {
			if (i + 1 == phu->IPnum) {
				cout << " ! ! ! 初始化“RespHead”时连接所有服务器失败 ! ! !" << endl;
				delete[] SendBuf;
				SendBuf = nullptr;
				return -108;
			}
		}
		else
		{
			break;
		}
	}
	sprintf_s(SendBuf, SENDBUFSIZE, "%s\r\n", ReqHead);
	cout << "----------------SendData3-----------------------\n" << SendBuf << "----------------end---------------------- " << endl;
	ret = send(sockid, SendBuf, strlen(SendBuf), 0);
	if (SOCKET_ERROR != ret) {
		cout << "ReqHead 数据发送成功" << endl;
	}
	else {
		cout << "ReqHead 数据发送失败" << endl;
		return -201;
		exit(1);
	}
	delete[] SendBuf;
	SendBuf = nullptr;
	char* RecvBuf = new char[RECVBUFSIZE];
	memset(RecvBuf, 0, RECVBUFSIZE);
	ret = recv(sockid, RecvBuf, RECVBUFSIZE, 0);
	/*cout << "----------------RecvData3:" << strlen(RecvBuf) << "-----------------------\n" << RecvBuf << endl << "----------------end---------------------- " << endl;*/
	sscanf_s(RecvBuf, "%[^\r]", sbuf, sizeof(sbuf));
	sbuf[strlen(sbuf)] = 0;
	if (0 != strncmp(sbuf, "HTTP/1.1 200 OK", 15)) {
		cout << "服务器应答报错:" << prh->Statusline << endl;
		delete[]RecvBuf;
		SendBuf = nullptr;
		closesocket(sockid);
		sockid = NULL;
		return -203;
	}
	char* pEnd = strstr(RecvBuf, "\r\n\r\n") + 4;
	CreateDirectory("./Download", nullptr);
	char fname[64];
	sprintf_s(fname, "./Download/%s", phu->Filename);
	fopen_s(&DFile, fname, "wb");
	if (NULL != DFile) {
		fwrite(pEnd, 1, ret - (int)(pEnd - RecvBuf), DFile);
		while ((ret = recv(sockid, RecvBuf, RECVBUFSIZE, 0)) > 0)
		{
			cout << ret << "-";
			Ltime2 = clock();
			if (Ltime2 - Ltime1 >= 1000) {
				cout << "下载速度:" << (Fsize2 - Fsize1) / 1024 << "KB/s,已下载:" << Fsize2 / 1024 << "KB,总大小:" << Fsize2 << " - " << prh->ContentLength << endl;
				Fsize1 = Fsize2;
				Ltime1 = Ltime2;
			}
			fwrite(RecvBuf, 1, ret, DFile);
			Fsize2 = ftell(DFile);
			if (Fsize2 >= prh->ContentLength) {
				cout << "下载速度:" << (Fsize2 - Fsize1) / 1024 << "KB/s,已下载:" << Fsize2 / 1024 << "KB,总大小:" << Fsize2 << " - " << prh->ContentLength << endl;
				break;
			}
			memset(RecvBuf, 0, RECVBUFSIZE);
		}
		
		fclose(DFile);
		DFile = nullptr;
	}
	else
	{
		cout << "创建文件失败" << endl;
	}
	delete[]RecvBuf;
	SendBuf = nullptr;
	closesocket(sockid);
	sockid = NULL;
	return 0;
}

//多线程下载
void Downloads(PVOID Pparam) {
	PPARAM param = (PPARAM)Pparam;
	long Fsize1 = 0;
	long Fsize2 = 0;
	long SStart = 0;
	WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
	int n = param->n;
	int h = n;
	++param->n;
	param->ftmp[n].DownComplete = false;
	ReleaseMutex(hMutex);							//------释放互斥锁------
	SOCKET sockid = INVALID_SOCKET;
	char sbuf[128];
	memset(sbuf, 0, 128);
	int ret = 0, err = 0;
	ULONGLONG SecondsStart = GetTickCount64();		//开始时间
	ULONGLONG SecondsNow = GetTickCount64();		//现在时间
	ULONGLONG SecondsLast = GetTickCount64();		//上一时间
	/*printf_s("Download2-ftmp%d=%p", n, param->ftmp[n].ThreadFtmp);*/
	rewind(param->ftmp[n].ThreadFtmp);
CreatS:
	SecondsNow = GetTickCount64();
	char* SendBuf = new char[SENDBUFSIZE];
	memset(SendBuf, 0, SENDBUFSIZE);
	 for (int i = 0; i < param->phu->IPnum; i++)
	{
		if (INVALID_SOCKET != (sockid = InitSocket(param->phu,h+i, n))) {
			strcpy_s(param->ftmp[n].ThreadIP,param->phu->IP[(h+i) % param->phu->IPnum]);
			break;
		}
		else
		{
			err = WSAGetLastError();
			cout << "WSAGetLastError() = " << err << endl;
			if (i + 1 >= param->phu->IPnum) {
				cout << " ! ! ! 下载线程[" << n << "]时连接所有服务器失败 ! ! !" << endl;
				delete[] SendBuf;
				SendBuf = nullptr;
				Sleep(3000);
				exit(1);
			}
		}
	}
	SStart = param->ftmp[n].ThreadStart + Fsize2;
	sprintf_s(SendBuf, SENDBUFSIZE, "%sRange: bytes=%ld-%ld\r\n\r\n", ReqHead, SStart, param->ftmp[n].ThreadEnd);
	//cout << "----------------SendData3-----------------------\n" << SendBuf << "----------------end---------------------- " << endl;
	while (SOCKET_ERROR == (ret = send(sockid, SendBuf, strlen(SendBuf), 0)))
	{
		cout << "ReqHead 数据发送失败,重新建联" << endl;
		closesocket(sockid);
		sockid = NULL;
		++h;
		Sleep(2000);
		goto CreatS;
	}
	//cout << "ReqHead 数据发送成功" << endl;
	delete[] SendBuf;
	SendBuf = nullptr;
	char* RecvBuf = new char[RECVBUFSIZE];
	memset(RecvBuf, 0, RECVBUFSIZE);
	SecondsNow = GetTickCount64();
	while (SOCKET_ERROR == (ret = recv(sockid, RecvBuf, SENDBUFSIZE, 0)))
	{
		/*err = WSAGetLastError();*/
		if (GetTickCount64() - SecondsNow >= 10000) {
			closesocket(sockid);
			sockid = NULL;
			++h;
			Sleep(1000);
			goto CreatS;
		}
		Sleep(60);
	}
	sscanf_s(RecvBuf, "%[^\r]", sbuf, sizeof(sbuf));
	sbuf[strlen(sbuf)] = 0;
	if (0 != strncmp(sbuf, "HTTP/1.1 206", 12)) {
		cout << "线程[" << n << "]服务器应答报错:" << sbuf << endl;
		closesocket(sockid);
		sockid = NULL;
		Sleep(1000);
		goto CreatS;
	}
	char* pEnd = strstr(RecvBuf, "\r\n\r\n");
	if (NULL != pEnd)
	{
		pEnd += 4;
		WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
		fwrite(pEnd, 1, ret - (int)(pEnd - RecvBuf), param->ftmp[n].ThreadFtmp);
		param->ftmp[n].ThreadSize = ftell(param->ftmp[n].ThreadFtmp);
		ReleaseMutex(hMutex);							//------释放互斥锁------
	}
	SecondsNow = GetTickCount64();
	SecondsLast = GetTickCount64();
	Fsize1 = Fsize2;
	while (ret = recv(sockid, RecvBuf, RECVBUFSIZE, 0))
	{
		if (INVALID_SOCKET == ret)
		{
			err = WSAGetLastError();
			if (GetTickCount64() - SecondsNow >= 20000) {
				WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
				cout << "线程["<<n<<"]20秒没有收到数据,重新发起连接" << endl << endl;
				ReleaseMutex(hMutex);							//------释放互斥锁------
				SecondsNow = GetTickCount64();
				closesocket(sockid);
				sockid = NULL;
				++h;
				Sleep(1000);
				goto CreatS;
			}
			/*cout << "WSAGetLastError() = " << err << endl;*/
			Sleep(100);
			continue;
		}
		SecondsNow = GetTickCount64();
		WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
		fwrite(RecvBuf, 1, ret, param->ftmp[n].ThreadFtmp);
		param->ftmp[n].ThreadSize = ftell(param->ftmp[n].ThreadFtmp);
		Fsize2 = ftell(param->ftmp[n].ThreadFtmp);
		ReleaseMutex(hMutex);							//------释放互斥锁------
		if (Fsize2 >= (param->prh->ContentBlock)) {
			param->ftmp[n].DownComplete = true;
			/*if (param->MaxSpeed) {
				param->MaxSpeed = param->ftmp->ThreadBlock / (GetTickCount64() - SecondsStart);
			}*/
			WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
			cout << "线程" << n << "文件下载完成" << endl << endl;
			ReleaseMutex(hMutex);							//------释放互斥锁------
			break;
		}
		/*cout <<n <<"-SJC:" << GetTickCount64() - SecondsLast <<" |ZDSD:"<< MaxSpeed * 4/10 <<" |SJSD"<<(float)(Fsize2 - Fsize1) / 1024 / 1024 << endl;*/
		if (GetTickCount64() - SecondsLast >= 40000) {
			SecondsLast = GetTickCount64();

			if (MaxSpeed != 0 && (float)(Fsize2 - Fsize1) / 1024 / 1024/40 < MaxSpeed / 20)
			{
				WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
				cout << "40秒内速度" << (float)(Fsize2 - Fsize1) / 1024 / 1024 / 40 <<",0.5MaxSpeed"<< MaxSpeed / 20 <<endl;
				cout << "线程[" << n << "]连续40秒下载速度低,重新发起连接" << endl << endl;
				ReleaseMutex(hMutex);							//------释放互斥锁------
				closesocket(sockid);
				sockid = NULL;
				++h;
				Sleep(2000);
				goto CreatS;
			}
			Fsize1 = Fsize2;
		}
		memset(RecvBuf, 0, RECVBUFSIZE);
		/*cout << "Download2【"<< n <<"】 已下载 :"<< Fsize << endl;*/
		
	}
	/*for (int i = 0; i < thread; i++)
	{
		if (param->ftmp[i].DownComplete == true && i != n)
		{

			if (param->ftmp[i].ThreadBlock * 2/10 <= param->ftmp[i].ThreadSize)
			{
				param->ftmp[n].ThreadEnd = param->ftmp[i].ThreadEnd;
				param->ftmp[i].ThreadEnd = param->ftmp[i].ThreadSize / 2 + param->ftmp[i].ThreadStart;
				param->ftmp[n].ThreadStart = param->ftmp[i].ThreadEnd + 1;
			}
		}
	}*/


	delete[]RecvBuf;
	SendBuf = nullptr;
	closesocket(sockid);
	sockid = NULL;
	return;
}

//打印下载速度
void PrintSpeed(PVOID Pparam) {
	PPARAM param = (PPARAM)Pparam;
	long Fsize1[16] = { 0 };
	long Fsize2[16] = { 0 };
	long FsizeNew = 0;
	long FsizeOld = 0;
	ULONGLONG SecondsStart = GetTickCount64();		//开始时间
	ULONGLONG SecondsNow = GetTickCount64();		//现在时间
	ULONGLONG SecondsSize = 0;						//运行时间
	/*DWORD Ltime1 = 0;
	DWORD Ltime2 = 0;
	DWORD Ltime = 0;*/
	float A = 0, B = 0, C = 0;
	bool b = true;
	while (true)
	{
		
		if (GetTickCount64() - SecondsNow >= 2000) {
			SecondsNow = GetTickCount64();
			SecondsSize = (SecondsNow - SecondsStart) / 1000;
			FsizeNew = 0;
			WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
			for (int i = 0; i < thread; i++)
			{
				FsizeNew += param->ftmp[i].ThreadSize;
			}
			A = (float)(FsizeNew - FsizeOld) / 1024 / 1024 / 2;
			B = (float)FsizeNew / 1024 / 1024;
			C = (float)param->prh->ContentLength / 1024 / 1024;
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 12);
			printf_s("下载 速度:%6.3fMB/s,已下载:%6.3fMB/s,总大小:%8.3fMB(bytes:%9ld-%-9ld) |(已用时间%02llu分%02I64u秒)\n", A, B, C, FsizeNew, param->prh->ContentLength, SecondsSize / 60, SecondsSize % 60);
			printf_s("最大单线速度:%6.3fMB/s,对应IP:%s\n", MaxSpeed,MaxIP);
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
			FsizeOld = FsizeNew;
			for (int i = 0; i < thread; i++)
			{
				
				A = (float)(param->ftmp[i].ThreadSize - Fsize1[i]) / 1024 / 1024 / 2;
				B = (float)param->ftmp[i].ThreadSize / 1024 / 1024;
				C = (float)param->ftmp[i].ThreadBlock / 1024 / 1024;
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 10);
				printf_s("线程%d[%s]速度:%6.3fMB/s,已下载:%6.3fMB/s,总大小:%8.3fMB(bytes:%9ld-%-9ld),块位置bytes:%9ld-%-9ld\n", i, param->ftmp[i].ThreadIP, A, B, C, param->ftmp[i].ThreadSize, param->ftmp[i].ThreadBlock, param->ftmp[i].ThreadStart, param->ftmp[i].ThreadEnd);
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
				Fsize1[i] = param->ftmp[i].ThreadSize;
				if (/*!param->ftmp[i].DownComplete &&*/ A > MaxSpeed) {
					MaxSpeed = A;
					strcpy_s(MaxIP, param->ftmp[i].ThreadIP);
				}
			}
			cout << endl;
			ReleaseMutex(hMutex);							//------释放互斥锁------

		}
		b = true;
		for (int i = 0; i < thread; i++)
		{
			b = b & param->ftmp[i].DownComplete;
			if (!b) {
				break;
			}
		}
		if (b) {
			WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
			FsizeNew = param->prh->ContentLength;
			A = (float)(FsizeNew - FsizeOld) / 1024 / 1024 / (GetTickCount64() - SecondsNow) * 1000;
			B = (float)param->prh->ContentLength / 1024 / 1024;
			C = (float)param->prh->ContentLength / 1024 / 1024;
			SecondsSize = (GetTickCount64() - SecondsStart) / 1000;
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 12);
			printf_s("下载 速度:%6.3fMB/s,已下载:%6.3fMB/s,总大小:%8.3fMB(bytes:%9ld-%-9ld) |(已用时间%02llu分%02I64u秒)\n", A, B, C, param->prh->ContentLength, param->prh->ContentLength, SecondsSize / 60, SecondsSize % 60);
			printf_s("最大单线速度:%6.3fMB/s,对应IP:%s\n", MaxSpeed, MaxIP);
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
			for (int i = 0; i < thread; i++) {
				Fsize2[i] = ftell(param->ftmp[i].ThreadFtmp);
				A = (float)(param->ftmp[i].ThreadSize - Fsize1[i]) / 1024 / 1024 / (GetTickCount64() - SecondsNow) * 1000;
				B = (float)param->ftmp[i].ThreadBlock / 1024 / 1024;
				C = B;
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 10);
				printf_s("线程%d[%s]速度:%6.3fMB/s,已下载:%6.3fMB/s,总大小:%8.3fMB(bytes:%9ld-%-9ld),块位置bytes:%9ld-%-9ld\n", i, param->ftmp[i].ThreadIP, A, B, C, param->ftmp[i].ThreadBlock, param->ftmp[i].ThreadBlock, param->ftmp[i].ThreadStart, param->ftmp[i].ThreadEnd);
				SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
			}
			cout << endl;
			ReleaseMutex(hMutex);							//------释放互斥锁------
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 12);
			cout << "\n\n* * * 文件下载完成,数据保存中,请等待..... * * *" << endl;
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
			break;
		}
		if (0 != errq)
		{
			break;
		}
	}
	return;
}

//保存输出文件
int Create_File(PRESPHEAD prh,PHTTPURL phu, PTHREADTMP ftmp) {
	FILE* DFile;
	int ret;
	bool b = true;
	CreateDirectory("./Download", nullptr);
	char tname[256];
	char fname[256];
	sprintf_s(tname, "./Download/%s.tmp", phu->Filename);
	sprintf_s(fname, "./Download/%s", phu->Filename);
	fopen_s(&DFile, tname, "wb");
	if (nullptr != DFile)
	{
		for (long i = 0; i < prh->ContentLength; i++)
		{
			char c = 0;
			fputc(c, DFile);
		}
		fclose(DFile);
		DFile = nullptr;
	}
	char path[256] = "";
	if (_getcwd(path, 256) == nullptr) {
		cout << "缓存(" << tname << ")文件创建完成\n" << endl;
	}
	WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
	cout << "缓存("<< path <<"\\Download\\"<< phu->Filename <<".tmp)文件创建完成\n" << endl;
	ReleaseMutex(hMutex);							//------释放互斥锁------
	while (true)
	{
		for (int i = 0; i < thread; i++)
		{
			if (ftmp[i].DownComplete&& !ftmp[i].SaveComplete)
			{
				Sleep(1000);
				fopen_s(&DFile, tname, "rb+");
				if (NULL != DFile) {
					fseek(DFile, ftmp[i].ThreadStart, SEEK_SET);
					char c = 0;
					rewind(ftmp[i].ThreadFtmp);
					while (true) //EOF是文件结束标志
					{
						c = fgetc(ftmp[i].ThreadFtmp);
						if (feof(ftmp[i].ThreadFtmp))
						{
							break;
						}
						fputc(c, DFile);
					}
					fclose(DFile);
					DFile = nullptr;
					WaitForSingleObject(hMutex, INFINITE);			//------开启互斥锁------
					ftmp[i].SaveComplete = true;
					cout << "线程" << i << "文件保存完成" << endl << endl;
					fclose(ftmp[i].ThreadFtmp);
					ftmp[i].ThreadFtmp = nullptr;
					tmpfile_s(&ftmp[i].ThreadFtmp);
					rewind(ftmp[i].ThreadFtmp);
					ReleaseMutex(hMutex);							//------释放互斥锁------
				}
				else
				{
					cout << "文件打开失败,检查文件,后按任意键继续" << endl << endl;
					i--;
					system("pause");
				}
			}
		}
		b = true;
		for (int i = 0; i < thread; i++) {
			b = b & ftmp[i].SaveComplete;
			if (!b) {
				break;
			}
		}
		if (b) {
			break;
		}
	}
	FILE* tmp;
	fopen_s(&tmp, fname, "r");
	if (nullptr != tmp)
	{
		fclose(tmp);
		char fbak[128] = "";
		sprintf_s(fbak, "%s.bak", fname);
		if (0 == rename(fname, fbak)) {
			cout << "文件已存在,已备份为:" << fbak << endl << endl;
		}
		else
		{
			remove(fname);
			cout << "文件已存在,备份失败,已被删除!!" << endl << endl;
		}
	}
	ret = rename(tname, fname);
	if (ret == 0)
	{
		printf("缓存文件转换成正式文件完成。\n");
		printf_s("文件位置: %s\Download\%s!\n", path, phu->Filename);
	}
	else
	{
		printf("错误:重命名失败,请手动删除文件后的【.tmp】");
	}
	
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 12);
	cout << "\n* * * 文件下载结束 * * *" << endl;
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
	return 0;
}

4.  main.cpp

#include"global.h"
HANDLE hMutex = CreateMutex(NULL, FALSE, "trw");		//创建互斥句柄,命名为“trw”
int errq = 0;
WSADATA WsaData;
int thread = 16;
float MaxSpeed = 0;
char MaxIP[16] = "";
int InitHttpurl(PHTTPURL phurl);								//初始化URL结构体
int InitRespHead(PRESPHEAD prh, PHTTPURL phu);					//初始化请求结构体
int InitThreadTmp(PRESPHEAD prh, PTHREADTMP ftmp);				//初始化线程结构体
int Download(PRESPHEAD prh, PHTTPURL phu);						//普通下载
void Downloads(PVOID Pparam);									//多线程下载
void PrintSpeed(PVOID Pparam);									//打印下载速度
int Create_File(PRESPHEAD prh, PHTTPURL phu, PTHREADTMP ftmp);	//保存文件


int main() {
	/*system("mode con cols=100 lines=100");*/
	HTTPURL HttpUil = { 0 };							//定义URL结构体
	THREADTMP FileTmp[16];								//定义线程缓存文件
	RESPHEAD RespHead ;									//定义请求消息结构体
	PARAM SParam = { NULL };							//定义线程参数结构第
	int S = 14, T = 10;									//定义打印变量
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), T);
	printf_s("┌────────────────────────────────────────────────┐\n");
	printf_s("%-50s%s\n%-7s", "│", "│", "│");
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), S);
	printf_s("%-43s", "作    者:仝 (TRW666)");
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), T);
	printf_s("%s\n%-50s%s\n%-7s", "│", "│", "│", "│");
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), S);
	printf_s("%-43s", "博客地址:https://blog.csdn.net/trw777");
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), T);
	printf_s("%s\n%-50s%s\n", "│", "│", "│");
	printf_s("└────────────────────────────────────────────────┘\n");
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);

	WSADATA WsaData;

	//初始换SOCKET绑定库
	if (0 != WSAStartup(MAKEWORD(2, 2), &WsaData)) {
		cout << "!!初始化socket库文件失败:[" << WSAGetLastError() << "]!!" << endl;
		Sleep(3000);
		return SOCKET_ERROR;
	}

	//运行始化URL函数
	if (0 != (errq = InitHttpurl(&HttpUil))) {
		cout << "错误码:ERR = " << errq << endl;
		system("pause");
		return errq;
	}
	
	//线程数赋值
	thread = TNUM * HttpUil.IPnum;
	cout <<"线程个数为:" << thread << endl;
	Sleep(1000);

	//运行初始化请求消息函数
	if (0 != (errq = InitRespHead(&RespHead, &HttpUil))) {
		cout << "错误码:ERR = " << errq << endl;
		system("pause");
		return errq;
	}

	//判断是否支持断点下载并执行
	if (RespHead.Breakpoint)
	{
		InitThreadTmp(&RespHead,FileTmp);
		SParam.phu = &HttpUil;
		SParam.prh = &RespHead;
		SParam.ftmp = FileTmp;
		SParam.n = 0;
		_beginthread(PrintSpeed, 0, (PVOID)&SParam);			//执行打印速度
		for (int i = 0; i < thread; i++)
		{
			_beginthread(Downloads, 0, (PVOID)&SParam);			//执行多线程下载
			while (SParam.n == i){
				Sleep(100);
			}
		}
		
		Sleep(2000);
		Create_File(&RespHead ,&HttpUil, FileTmp);				//执行保存文件
	}
	else
	{
		Download(&RespHead, &HttpUil);							//执行普通下载
	}
	WSACleanup();
	if (0 != errq)
	{
		cout << "错误码:ERR = " << errq << endl;
	}
	system("pause");
	return errq;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值