使用C++基于Socket编程实现文件下载(改进-封装成类)

news/2024/7/3 16:55:57

请添加图片描述
使用:
(1)首先运行服务端,待服务端运行起来;
(2)最后运行客户端,输入要传输文件到哪个目标机器的IP地址;
(3)输入传输文件的路径及文件(完成的路径),其中包含文件的类型,也就是后缀需要包含(代表需要传输文件的类型)。
例如:E:/Data(D)/Cat_and_Dog/dog.jpg
参考博主:https://blog.csdn.net/sinat_23118323/article/details/71024351

客户端:

#pragma once
#ifndef _LOAD_SOCKET_H_
#define _LOAD_SOCKET_H_
#include <WinSock2.h> //windows socket的头文件
#include <Windows.h>
#include <iostream>
#include <thread>
#include <process.h>
#include<mutex>
#include<string>
#include<process.h>
#define MAXBYTES 300*1024
#define err(errMsg) printf("[error] %s failed,code %d\
line:%d\n",errMsg, WSAGetLastError(),__LINE__)
#pragma comment(lib, "ws2_32.lib") //连接winsock2.h的静态库文件using namespace std;
class Socket {private:SOCKET clientSock;int id;char wb_file[MAXBYTE];char wb_files[MAXBYTE];char wb_file_s[MAXBYTE] ;string ip;public://获取当前日期void OBTION_TIME();//获取开始时间double START_TIME();//获取结束时间double END_TIME();//获取文件大小void getByteSize(long size);static DWORD WINAPI transmmit(const LPVOID arg);//输入文件void INPUT_FILE(string file1, string file2);int MAIN_SOCKET();//输入IPint INPUT_IP(string ip);//判断输入的IP地址是否可以连接int JUDEG_IP(string ip);//清除库void CLEAR();
};
//定义结构体用来设置
typedef struct my_file
{SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信sockaddr_in clientAddr; //用于保存客户端的socket地址int id; //文件块的序号
}F;
#endif // !_LOAD_SOCKET_H_

#include "load_socket.h"using namespace std;
mutex m;
//获取当前日期
void Socket::OBTION_TIME() {SYSTEMTIME start; //windows.h中  GetLocalTime(&start);//time.h的tm结构体一样的效果  cout << start.wYear << "/" << start.wMonth << "/" << start.wDay << " " << start.wHour << ":" << start.wMinute << ":" << start.wSecond << endl;
}
//获取开始时间
double Socket::START_TIME() {DWORD start_time;start_time = GetTickCount64();return (double)start_time;
}
//获取结束时间
double Socket::END_TIME() {DWORD end_time;end_time = GetTickCount64();return double(end_time);
}
//获取文件大小
void Socket::getByteSize(long size) {long rest = 0;if (size < 1024) {cout << size << "B" << endl;return;}else {size /= 1024;}if (size < 1024) {cout << size << "KB" << endl;return;}else {rest = size % 1024;size /= 1024;}if (size < 1024) {size *= 100;cout << (size / 100) << "." << (rest * 100 / 1024 % 100) << "MB" << endl;return;}else {size = size * 100 / 1024;cout << (size / 100) << "." << (size % 100) << "GB" << endl;return;}
}//LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递)
DWORD WINAPI Socket::transmmit(const LPVOID arg)
{//上锁是为了方便看输出m.lock();Socket* soc = (Socket*)arg;cout << "测试开始,等待服务端发送消息..." << endl;char revdata[1024] = { 0 };char file_name[100] = { 0 };int ret = recv(soc->clientSock, revdata, MAXBYTE, 0);char file_names[1024] = { 0 };if (ret > 0) {revdata[ret] = '\0';int i = 0;for (i = 0; i < strlen(revdata); i++) {if (revdata[i] == 'E' && revdata[i + 1] == 'S' && revdata[i + 2] == 'C') {break;}file_name[i] = revdata[i];}file_name[i] = '\0';}else {cout << "文件下载出错" << endl;}ret = recv(soc->clientSock, file_names, MAXBYTE, 0);if (ret > 0) {file_names[ret] = '\0';for (int i = 0; i < strlen(file_names); i++) {if (file_names[i] == 'E' && file_names[i + 1] == 'S' && file_names[i + 2] == 'C') {break;}}cout << "开始下载文件" << endl;}//文件路径unsigned long long len_file = 0;FILE* fp = fopen(file_name, "rb");if (fp == NULL) {cout << "文件" << file_name << "出错或不存在" << endl;}else {cout << "发送文件时间: ";soc->OBTION_TIME();double start_time = soc->START_TIME();char Buffer[MAXBYTES] = { 0 }; //文件缓冲区unsigned long long size = 0; //读取的文件长度while ((size = fread(Buffer, sizeof(char), MAXBYTES, fp)) > 0) {//返回非0值表示send错误if (send(soc->clientSock, Buffer, size, NULL) < 0){cout << "下载出错,请检查网络配置。" << endl;}len_file += size;//每次读取完之后清空缓存区,以便下一块文件读入memset(&Buffer, 0, MAXBYTES);}const char* t = "end";send(soc->clientSock, t, strlen(t), NULL);cout << soc->id << "线程已成功发送" << file_name << endl;cout << "下载文件大小: ";len_file = (unsigned long long)len_file;soc->getByteSize(len_file);cout << "文件下载结束时间: ";soc->OBTION_TIME();double end_time = soc->END_TIME();double currentTime = 0;currentTime = end_time-start_time;cout << "文件下载耗时: " << currentTime << endl;fclose(fp);}m.unlock();return 0;
}
void Socket::INPUT_FILE(string file1,string file2) {for (int i = 0; i < file1.length(); i++) {wb_files[i] = file1[i];}wb_files[file1.length()] = '\0';int len = strlen(wb_files);wb_files[len] = 'E';wb_files[len + 1] = 'S';wb_files[len + 2] = 'C';wb_files[len + 3] = '\0';send(clientSock, wb_files, strlen(wb_files), 0);for (int i = 0; i < file2.length(); i++) {wb_file_s[i] = file2[i];}wb_file_s[file2.length()] = '\0';
}
int  Socket::INPUT_IP(string ip) {const char* ips = ip.c_str();WSADATA wsadata;//WSA-windows socket ansyc windows的异步套接字 2.2版本的if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) {err("WSAStartup");return 0;}//建立连接//客户端socket//加载winsock库clientSock = socket(AF_INET, SOCK_STREAM, 0);if (clientSock == INVALID_SOCKET) {err("SOCKET");return 0;}//初始化socket信息sockaddr_in clientAddr;memset(&clientAddr, 0, sizeof(SOCKADDR));//clientAddr.sin_addr.s_addr = htonl(INADDR_ANY);clientAddr.sin_family = AF_INET;clientAddr.sin_port = htons(2017);//将本地字节序转换为网络字节序,大端和小端存储clientAddr.sin_addr.s_addr = inet_addr(ips);if (connect(clientSock, (SOCKADDR*)&clientAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {err("connect");return 0;}return 1;
}
int Socket::JUDEG_IP(string ip) {if (INPUT_IP(ip)) {return true;}else{return false;}
}
int Socket::MAIN_SOCKET(){char Buffer[MAXBYTES] = { 0 }; // 文件缓冲区int i = 0;for (i = 0; i < strlen(wb_file_s) ; i++) {wb_file[i] = wb_file_s[i];}wb_file[i] = '\0';FILE* fp = fopen(wb_file, "wb");//如果录入文件不存在的话就创建一个新的文件if (fp == NULL) {fp = fopen(wb_file, "w");}unsigned long long len_file = 0;if (fp == NULL) {cout << "操作文件时出错" << endl;system("pause");}else {cout << "下载文件时间: ";OBTION_TIME();double start_time = START_TIME();unsigned long long g_fileSize = 0;char rev_buffer[MAXBYTES] = { 0 };//接收文件的长度int rev_len = recv(clientSock, rev_buffer, MAXBYTE, 0);if (rev_len > 0) {rev_buffer[rev_len] = '\0';for (int i = 0; i < strlen(rev_buffer); i++) {g_fileSize = g_fileSize * 10 + ((unsigned long long)rev_buffer[i] - 48);}}memset(&Buffer, 0, MAXBYTES);unsigned long long size = 0;//当成功接收文件(size > 0)时,判断写入的时候文件长度是否等于接收的长度while ((size = recv(clientSock, Buffer, MAXBYTES, 0)) > 0) {if (Buffer[size - 3] == 'e' && Buffer[size - 2] == 'n' && Buffer[size - 1] == 'd'){char buffer[MAXBYTES] = { 0 };for (int i = 0; i < strlen(Buffer) - 3; i++) {buffer[i] = Buffer[i];}len_file += size - 3;size -= 3;if (fwrite(buffer, sizeof(char), size, fp) < size) {cout << "下载出错,部分文件缺失。" << endl;break;}cout.width(3);//i的输出为3位宽if ((len_file * 100 / g_fileSize) % 5 > 0) {cout << (len_file * 100 / g_fileSize) << "%";cout << "\b\b\b\b";//回删三个字符,使数字在原地变化}break;}else {if (fwrite(Buffer, sizeof(char), size, fp) < size) {cout << "下载出错,部分文件缺失。" << endl;}len_file += size;}cout.width(3);//i的输出为3位宽if ((len_file * 100 / g_fileSize) % 5 > 0) {cout << (len_file * 100 / g_fileSize) << "%";cout << "\b\b\b\b";//回删三个字符,使数字在原地变化}//清空缓存区以便下一次接收memset(&Buffer, 0, MAXBYTES);}cout << "下载完成..." << endl;cout << "下载文件大小: ";len_file = (unsigned long long)len_file;getByteSize(len_file);cout << "文件结束下载时间: ";OBTION_TIME();double end_time = END_TIME();double currentTime = 0;currentTime = (double)(end_time - start_time) / CLOCKS_PER_SEC;cout << "下载文件耗时: " << currentTime <<"s"<< endl;fclose(fp);}return 0;
}
void Socket::CLEAR() {关闭网络库 closesocket(clientSock);if (WSACleanup() != 0) {err("WSACleanup");return ;}cout << "客户端连接已关闭。" << endl;system("pause");}

#include"load_socket.h"
int main() {Socket* so = new Socket();string ip;while (true) {cout << "请输入IP地址: ";cin >> ip;bool flag = so->JUDEG_IP(ip);if (flag == true) {break;}else{cout << "输入的IP地址不存在或者错误" << endl;}}cout << "已建立连接。" << endl;string file1, file2;while (true) {cout << "首先请客户端输入想要下载的文件路径: ";cin >> file1;cout << endl;cout << "其次请服务端输入想要下载的文件保存路径: ";cin >> file2;so->INPUT_FILE(file1, file2);so->MAIN_SOCKET();}so->CLEAR();
}

服务端:

#pragma once
#ifndef _LOAD_SOCKET_H_
#define _LOAD_SOCKET_H_
#include <WinSock2.h> //windows socket的头文件
#include <Windows.h>
#include <iostream>
#include <thread>
#include <process.h>
#include<mutex>
#include<string>
#include<process.h>
#define MAXBYTES 300*1024
#define err(errMsg) printf("[error] %s failed,code %d\
line:%d\n",errMsg, WSAGetLastError(),__LINE__)
#pragma comment(lib, "ws2_32.lib") //连接winsock2.h的静态库文件using namespace std;
class Server {private:SOCKET servSocket;SOCKET clientSock;int id;public://获取当前日期void OBTION_TIME();//获取开始时间double START_TIME();//获取结束时间double END_TIME();//获取文件大小void getByteSize(unsigned long long size);static DWORD WINAPI transmmit(const LPVOID arg);//清除库void CLEAR();int MAIN_SERVER();
};
//定义结构体用来设置
typedef struct my_file
{SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信sockaddr_in clientAddr; //用于保存客户端的socket地址int id; //文件块的序号
}F;
#endif // !_LOAD_SOCKET_H_

#include "load_server.h"mutex m;
//获取当前日期
void Server::OBTION_TIME() {SYSTEMTIME start; //windows.h中  GetLocalTime(&start);//time.h的tm结构体一样的效果  cout << start.wYear << "/" << start.wMonth << "/" << start.wDay << " " << start.wHour << ":" << start.wMinute << ":" << start.wSecond << endl;
}
//获取开始时间
double Server::START_TIME() {DWORD start_time;start_time = GetTickCount64();return (double)start_time;
}
//获取结束时间
double Server::END_TIME() {DWORD end_time;end_time = GetTickCount64();return double(end_time);
}
//获取文件大小
void Server::getByteSize(unsigned long long size) {unsigned long long rest = 0;if (size < 1024) {cout << size << "B" << endl;return;}else {size /= 1024;}if (size < 1024) {cout << size << "KB" << endl;return;}else {rest = size % 1024;size /= 1024;}if (size < 1024) {size *= 100;cout << (size / 100) << "." << (rest * 100 / 1024 % 100) << "MB" << endl;return;}else {size = size * 100 / 1024;cout << (size / 100) << "." << (size % 100) << "GB" << endl;}
}//LPVOID是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量(一般作为参数传递)
DWORD WINAPI Server::transmmit(const LPVOID arg) {//上锁是为了方便看输出m.lock();Server* so = (Server*)arg;/*获取文件的序号int file_id = temp->id;获取客户机的端口号ntohs(temp -> clientAddr.sin_port);*/cout << "测试开始,等待客户端发送消息..." << endl;char revdata[1024] = { 0 };char file_name[100] = { 0 };//文件路径 int ret = recv(so->clientSock, revdata, MAXBYTE, 0);char file_names[1024] = { 0 };if (ret > 0) {revdata[ret] = '\0';int i = 0;for (i = 0; i < strlen(revdata); i++) {if (revdata[i] == 'E' && revdata[i + 1] == 'S' && revdata[i + 2] == 'C') {break;}file_name[i] = revdata[i];}file_name[i] = '\0';cout << "接收到下载文件消息" << endl;}else {cout << "文件下载出错" << endl;}FILE* fp = fopen(file_name, "rb");unsigned long long len_file = 0;if (fp == NULL) {cout << "文件" << file_name << "出错或不存在" << endl;}else {unsigned long long g_fileSize = 0;cout << "下载文件时间: ";so->OBTION_TIME();double start_time = so->START_TIME();struct _stat64 st;_stat64(file_name, &st);g_fileSize = st.st_size;string send_file_len;send_file_len = to_string(g_fileSize);send(so->clientSock, send_file_len.c_str(), send_file_len.length(), 0);char Buffer[MAXBYTES] = { 0 }; //文件缓冲区unsigned long long size = 0; //读取的文件长度while ((size = fread(Buffer, sizeof(char), MAXBYTES, fp)) > 0) {//返回非0值表示send错误if (send(so->clientSock, Buffer, size, NULL) < 0) {cout << "下载出错,请检查网络配置。" << endl;break;}len_file += size;cout.width(3);//i的输出为3位宽if ((len_file * 100 / g_fileSize) % 5 > 0) {cout << (len_file * 100 / g_fileSize) << "%";cout << "\b\b\b\b";//回删三个字符,使数字在原地变化}size = 0;//每次读取完之后清空缓存区,以便下一块文件读入memset(&Buffer, 0, MAXBYTES);}const char* t = "end";send(so->clientSock, t, strlen(t), NULL);cout << so->id << "线程已成功发送" << file_name << endl;cout << "下载文件大小: ";len_file = (unsigned long long)len_file;so->getByteSize(len_file);cout << "文件下载结束时间: ";double end_time = so->END_TIME();so->OBTION_TIME();double currentTime = 0;currentTime =(double)(end_time-start_time)/ CLOCKS_PER_SEC;cout << "下载文件耗时: " << currentTime <<"s"<< endl;fclose(fp);}m.unlock();return 0;
}
int Server::MAIN_SERVER() {//加载网络库WSADATA wsaData;//第一个参数是winsocket load的版本号(2.2)if (WSAStartup(MAKEWORD(2, 3), &wsaData) != 0) {err("WSAStartup");return 0;}//创建服务器端的socket(协议族, sokcet类型)servSocket = socket(AF_INET, SOCK_STREAM, 0);//如果改成SOCK_DGRAM则使用UDPif (servSocket == INVALID_SOCKET) {err("SOCKET");return 0;}sockaddr_in servAddr; //服务器的socket地址,包含sin_addr表示IP地址,sin_port保持端口号和sin_zero填充字节memset(&servAddr, 0, sizeof(SOCKADDR)); //初始化socket地址servAddr.sin_family = AF_INET; //设置使用的协议族servAddr.sin_port = htons(2017); //设置使用的端口servAddr.sin_addr.s_addr = INADDR_ANY; //define s_addr = S_un.S_addr//将之前创建的servSocket和端口,IP地址绑定if (bind(servSocket, (SOCKADDR*)&servAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {err("bind");return 0;}listen(servSocket, 1); //监听服务器端口cout << "等待连接..." << endl;sockaddr_in clntAddr;int nSize = sizeof(clntAddr);clientSock = accept(servSocket, (SOCKADDR*)&clntAddr, &nSize);while(true){//“句柄” 类似指针, 但通过指针可读写对象, 通过句柄只是使用对象;HANDLE hThread[2]; //获取句柄memset(hThread, 0, sizeof(hThread));//最多可以接受20个请求for (int i = 0; i < 1; i++) {if (clientSock == INVALID_SOCKET) {err("accept");break;}//通过句柄创建子线程,使用 API 的 CreateThread, 它执行完入口函数后会自动退出, 无需 ExitThread;hThread[i] = CreateThread(NULL, 0, &transmmit, this, 0, NULL);}//等待子线程完成WaitForMultipleObjects(1, hThread, TRUE, INFINITE);cout << "错误代码: " << WSAGetLastError() << endl;}return 0;
}
void Server::CLEAR(){关闭网络库 closesocket(servSocket);closesocket(clientSock);if (WSACleanup() != 0) {err("WSACleanup");return;}cout << "客户端连接已关闭。" << endl;system("pause");}

#include "load_server.h"
int main() {Server* so = new Server();so->MAIN_SERVER();so->CLEAR();return 0;
}

http://lihuaxi.xjx100.cn/news/285837.html

相关文章

北大高材生割美国韭菜被FBI通缉?本人回应予以否认

一位国内私募公司创始人疑似因策划操纵股票期货合约&#xff0c;被美国司法部起诉。11月7日&#xff0c;美国司法部和商品期货交易委员会&#xff08;CFTC&#xff09;发布通告称&#xff0c;高频交易公司Tower Research Capital同意签署了一项延期起诉协议&#xff08;DPA&…

Codeforces Round #648 (Div. 2)题解 A-D

文章目录A - Matrix GameB - Trouble SortC - Rotation MatchingD - Solve The MazeA - Matrix Game 题解&#xff1a;其实这个题细细一想&#xff0c;剩下可以化1的位置的数量就是整行不包含1的行数或整列不包含1的列数的两者最小值&#xff0c;最后在判断一下奇偶即可。 /*K…

分类讨论 ---- 2021 icpc 沈阳 L Linear Fractional Transformation (思维题)

题目链接 题目大意 给你一个线性变换函数f(z)azbczdf(z)\frac{azb}{czd}f(z)czdazb​,现在把取值范围扩展到复数域。给你3个等式 f(z1)w1f(z_1)w_1f(z1​)w1​ f(z2)w2f(z_2)w_2f(z2​)w2​ f(z3)w3f(z_3)w_3f(z3​)w3​ 问你f(z0)f(z_0)f(z0​)是多少&#xff1f; 解题思路&a…

用Python做一个翻译软件

来源 | Ahab杂货铺&#xff08;ID&#xff1a;PythonLearningCamp&#xff09;前两天吃了平哥的一波狗粮&#xff0c;他给女朋友写了一个翻译软件&#xff0c;自己真真切切的感受到了程序员的浪漫。在学习requests请求的时候做过类似的Demo&#xff0c;给百度翻译发送一个post请…

[分享]iOS开发-UI篇:CAlayer层的属性

iOS开发UI篇—CAlayer层的属性 一、position和anchorPoint 1.简单介绍 CALayer有2个非常重要的属性&#xff1a;position和anchorPoint property CGPoint position; 用来设置CALayer在父层中的位置 以父层的左上角为原点(0, 0) property CGPoint anchorPoint; 称为“定位点”、…

云服务新手必备的SaaS成本管理技巧

企业加速部署基于云的SaaS产品可以归因于很多因素-全球疫情蔓延、过时的软件和硬件&#xff0c;甚至仅仅是企业数据中心中的简单设备故障。 但是很快–如果还没有&#xff0c;你的第一笔账单也将到来。如果你是云服务新手&#xff0c;并且没有适当的SaaS成本管理控制措施&#…

她破解哈希函数算法:坚持10年做一件事一定能做成

今年9月7日&#xff0c;2019未来科学大奖揭晓&#xff0c;这项设立4年的大奖迎来首位女性得主——密码学家王小云。在信息时代&#xff0c;金融服务、网络安全等背后都离不开密码系统的“护航”。在大众眼中&#xff0c;密码带着神秘色彩&#xff0c;王小云破解和设计密码的经历…

使用C++的Socket实现从客户端到服务端,服务端到客户端传输文件

使用&#xff1a; &#xff08;1&#xff09;首先运行服务端&#xff0c;待服务端运行起来&#xff1b; &#xff08;2&#xff09;最后运行客户端&#xff0c;输入要传输文件到哪个目标机器的IP地址&#xff1b; &#xff08;3&#xff09;输入传输文件的路径及文件&#xff0…