第一季简单实现了下载功能: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] = { "" };            //文本IPint    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;                    //线程IDchar 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;}//域名转换IPHostIp = 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;
}

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

  1. C 语言http GET请求 超小纯净下载工具 (暂时只支持http,支持大文件下载)第三季

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

  2. C 语言 GET请求 超小纯净下载工具 (支持http、https)第四季

    第一季简单实现了下载功能:C 语言http GET请求 超小纯净下载工具 (暂时只支持http)第一季_trw777的博客-CSDN博客 第二季主要修改功能:​​​​​​C 语言http GET请求 ...

  3. wifi 联想小新_WiFi 6网卡?官方辟谣联想小新Air 14 2020只支持WiFi 5

    [CNMO新闻]遐想小新Air 14 2020于2月20日正式宣布,今朝已经有用户陆连续续收到了小新Air14 2020新机.只不外收到新机后有用户发明,在鲁大家硬件检测中,将小新Air 14 202 ...

  4. 打怪小游戏(暂时只支持主线任务、刷怪和作者商店)

    #include<bits/stdc++.h> #include<stdlib.h> #include<stdio.h> #include<time.h> ...

  5. 微信开发者工具无法选择预览和真机调试_小程序开发 第二篇:使用微信小程序开发者工具、wepy框架初始化项目...

    1.微信小程序开发者工具 使用: 小程序原生开发:直接使用小程序开发者工具打开项目即可 小程序框架开发:首选官方提供类vue.js开发框架 wepy.js ,备选 mpVue.我们选择的是 wepy ...

  6. 【uni-app】只支持在微信小程序运行的 导入外部3d模型

    1.解决问题 uniapp 导入3d模型,在微信小程序端运行.只支持在微信小程序端使用,若要支持 h5 和 APP端,可以查看这篇,点击这里 只导入了3d模型,未设置让模型动. 2.导入模型格式 gl ...

  7. 微信小程序开发制作 | 小程序开发者工具功能介绍

    小程序开发者工具是微信官方提供的用于开发和调试小程序的工具.它支持 Windows 和 Mac 两种操作系统,并提供了许多实用的功能,使得小程序开发者能够快速地开发和调试小程序. 下面是小程序开发者工 ...

  8. c语言 串口 封装,首颗超小封装的串口(UART)转PWM转换芯片ZWI10A

    全球首颗超小封装的串口(UART)转PWM控制芯片SOC. 功能概述 ● PWM输出特性 - PWM输出范围150HZ-200KHZ. - PWM频率为16位精度. -占空比调节方式(0-255). ...

  9. 【牛客刷题】上手用C语言写一个三子棋小游戏超详解哦(电脑优化)

    作者:[南航科院小张 南航科院小张的博客 专栏:从c语言的入门到进阶 学习知识不只是要懂,还要会用:想要找到好的工作,这里给大家介绍一件可以斩获诸多大厂offer的利器–牛客网 点击免费注册和我一起开 ...

最新文章

  1. 利用java打印正三角形_JAVA一层for循环实现打印正三角形和到三角形
  2. php5.3的新特性
  3. 干 MySQL 两千万数据的大表优化解决过程,三种厉害的解决方案
  4. 2018年SIAF 广州国际工业自动化技术及装备展览会下周隆重开幕
  5. 吴恩达深度学习4.1练习_Convolutional Neural Networks_Convolution_model_Application_2
  6. 【转载】Web前端框架图
  7. Python设置32位环境
  8. SPSS Modeler 数据整理之变量设定 (指南 第三章)
  9. 登陆163邮箱 验证邮箱帐号密码是否正确
  10. Pair project(刘昊岩11061156 黄明源11061186)
  11. PYTHON开发毕业设计做什么好鸭?
  12. 血型(输血-受血)匹配数电设计
  13. java斗地主发牌_java实现斗地主发牌系统
  14. LVGL lv_line线条(15)
  15. 第二章 Selenium-API操作
  16. PyCharm打包可执行文件方法
  17. stm32f103zet6 hello world
  18. Android:多分辨率适配
  19. 5个常用的开源聊天应用
  20. 一个模型,帮你提升行动力

热门文章

  1. hadoop之HDFS HA(高可用)架构安装部署
  2. Qt商业组件Qtitan系列大更新:适用于Qt5.15.2和Visual Studio2019
  3. 用AD设计PCB时,原理图是从别的工程中复制过来的,在工程原理图 Compile时会出现 Unique ID 重复的错错...
  4. 新概念2 课文和单词(16)
  5. 近红外二区荧光AgAuSe合金化量子点,第二近红外窗口(NIR-II,900-1700 nm)荧光量子点
  6. 17-equ伪指令和jmp指令
  7. MySQL Workbench使用及教程
  8. 美国商学院申请条件知多少,雅思专家来解惑
  9. 黄仁勋台北演讲分享 不论是为了追逐食物而跑,或不被他人当作食物而跑,都要尽量保持奔跑,别缓步前进。
  10. 软件构造Lab2实验总结