第一季简单实现了下载功能:https://blog.youkuaiyun.com/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.youkuaiyun.com/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;
}