14.9 Socket 高效文件传输
网络上的文件传输功能也是很有必要实现一下的,网络传输文件的过程通常分为客户端和服务器端两部分。客户端可以选择上传或下载文件,将文件分块并逐块发送到服务器,或者从服务器分块地接收文件。服务器端接收来自客户端的请求,根据请求类型执行对应的操作,并根据发送的文件名或其他标识来确定要传输的文件。
在实现文件传输之前,需要先打开要传输的文件,并获取文件的大小信息,也可以通过其他方式获取文件的信息。在客户端和服务器端都准备就绪后,可以通过套接字来发送文件数据。在传输文件的过程中,可以将文件分解为若干个数据包进行传输,以减少数据传输中的丢包或传输错误。每个数据包的长度可以根据实际情况进行选择,通常选择1024字节或更大,也可以设置成更小的值。传输文件的过程中,还需要实现一定的错误处理机制,例如检测传输过程中的超时、丢包、不完整数据等情况,并在必要时进行错误重传或协商其他解决方案。
首先无论时服务端还是客户端都需要封装两个函数,其中GetFileName()函数用于当用户传入文件的具体路径信息时自动获取到该文件的文件名,第二个函数GetFileSize()则用于传入文件路径并自动获取到该文件的字节数。
// 传入路径得到文件名
char* GetFileName(char* Path)
{if (strchr(Path, '\\')){char ch = '\\';char* ref = strrchr(Path, ch) + 1;return ref;}else{char ch = '/';char* ref = strrchr(Path, ch) + 1;return ref;}
}// 获取文件大小
int GetFileSize(std::string FileName)
{FILE* pointer = NULL;pointer = fopen(FileName.c_str(), "rb");if (pointer != NULL){fseek(pointer, 0, SEEK_END);int size = ftell(pointer);fclose(pointer);return size;}return 0;
}
接着我们来看一下RecvFile()接收文件函数是如何实现的,首先第一个发送用于向服务端发出我需要下载具体的那个目录下的文件,接着服务端会返回该目录文件的长度,此时我们通过fopen()创建一个新文件,并以此循环接收该文件的长度,每次接收成功后自动的fwrite写出到文件中,当文件被接收完毕后,则通过fclose(pointer)保存并关闭文件。
// 接收文件
bool RecvFile(SOCKET ptr, char* LocalPath, char* RemoteFile)
{// 发送需要下载的文件路径send(ptr, RemoteFile, strlen(RemoteFile), 0);// 接收文件长度long long file_size = 0;recv(ptr, (char*)&file_size, sizeof(int), 0);if (file_size <= 0){return false;}// 保存文件到指定目录下char *file_name = GetFileName(RemoteFile);char file_all_name[1024] = { 0 };strcat(file_all_name, LocalPath);strcat(file_all_name, file_name);std::cout << "生成保存路径: " << file_all_name << std::endl;FILE* pointer = fopen(file_all_name, "wb");char buffer[1024] = { 0 };if (pointer != NULL){long long length = 0;long long total_length = 0;// 循环接收字节数据,每次接收1024字节while ((length = recv(ptr, buffer, 1024, 0)) > 0){// 写出文件并判断是否写出成功if (fwrite(buffer, sizeof(char), length, pointer) < length){break;}// 每次累加递增total_length += length;memset(buffer, 0, 1024);// 判断文件长度是否全部接收完毕if (total_length >= file_size){std::cout << "文件接收完毕, 接收字节数: " << total_length << std::endl;fclose(pointer);return true;}}fclose(pointer);}return false;
}
对于SendFile()发送文件而言首先我们接收到客户端传来的文件路径,并通过该路径得到该文件的具体长度,第一次调用发送函数将文件的长度传递给客户端,此时打开我们所需要发送的文件,并通过循环的方式向客户端传输,当数据包传输完毕后则自动关闭文件。
// 发送指定文件
bool SendFile(SOCKET ptr)
{// 接收文件路径char file_path[1024] = { 0 };recv(ptr, file_path, 1024, 0);// 得到文件长度并发送给服务端long long file_size = GetFileSize(file_path);if (file_size <= 0){return false;}send(ptr, (char*)&file_size, sizeof(int), 0);std::cout << "发送文件长度: " << file_size << std::endl;// 循环发送数据char buffer[1024] = { 0 };FILE* pointer = fopen(file_path, "rb");if (pointer != NULL){long long length = 0;long long total_length = 0;// 循环发送数据while ((length = fread(buffer, sizeof(char), 1024, pointer)) > 0){send(ptr, buffer, length, 0);memset(buffer, 0, 1024);total_length += length;}if (total_length == file_size){return true;}}return false;
}
14.9.1 服务端实现
如下代码展示了如何使用Winsock进行TCP协议的文件传输。首先使用WSAStartup函数对Winsock库进行初始化。然后创建一个socket,设置IP地址、端口号等信息,并将该socket和本地服务端的地址绑定起来。接下来对该socket进行监听,等待客户端的连接请求。
当有客户端连接请求到来时,accept函数会接收请求,并创建一个新的socket与客户端进行通信。在与客户端通信的过程中,可以通过send和recv函数进行数据的传输,实现文件的上传和下载功能。此处的代码调用RecvFile函数,该函数为自定义实现的接收文件函数,负责接收数据并将接收到的文件存储到指定的路径下。
int main(int argc, char* argv[])
{WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)exit(1);// 声明并初始化一个服务端(本地)的地址结构 sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_addr.S_un.S_addr = INADDR_ANY;server_addr.sin_port = htons(8087);// 创建socket SOCKET m_Socket = socket(AF_INET, SOCK_STREAM, 0);if (SOCKET_ERROR == m_Socket)exit(1);// 绑定socket和服务端(本地)地址 if (SOCKET_ERROR == bind(m_Socket, (LPSOCKADDR)&server_addr, sizeof(server_addr)))exit(1);// 监听 if (SOCKET_ERROR == listen(m_Socket, 10))exit(1);sockaddr_in client_addr;int client_addr_len = sizeof(client_addr);SOCKET m_New_Socket = accept(m_Socket, (sockaddr*)&client_addr, &client_addr_len);// 接收远程d://lyshark.exe放到本地的d://11g/目录下bool ref = RecvFile(m_New_Socket, (char*)"d://11g/", (char*)"d://lyshark.exe");std::cout << "接收状态: " << ref << std::endl;closesocket(m_New_Socket);closesocket(m_Socket);WSACleanup();system("pause");return 0;
}
14.9.2 客户端实现
如下客户端代码实现了一个基于TCP协议的文件传输客户端。首先使用WSAStartup函数对Winsock库进行初始化。然后创建一个socket,并设置服务端的IP地址和端口号。之后通过connect函数与服务端建立连接,连接成功后调用SendFile函数进行文件传输,将指定的文件发送到服务端。文件传输完成后,关闭socket连接,清除Winsock资源。
int main(int argc, char* argv[])
{while (true){WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)exit(1);// 创建socket SOCKET c_Socket = socket(AF_INET, SOCK_STREAM, 0);//指定服务端的地址 sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");server_addr.sin_port = htons(8087);if (SOCKET_ERROR != connect(c_Socket, (LPSOCKADDR)&server_addr, sizeof(server_addr))){bool ref = SendFile(c_Socket);std::cout << "文件发送状态: " << ref << std::endl;}closesocket(c_Socket);WSACleanup();Sleep(1000);}return 0;
}
文件传输功能代码就这些,其实理解起来并不难,读者可自行编译并运行上述代码,运行后则可接收远程d://lyshark.exe文件,并放到本地的d://11g/目录下,输出效果图如下;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/323aa250.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章:
14.9 Socket 高效文件传输
网络上的文件传输功能也是很有必要实现一下的,网络传输文件的过程通常分为客户端和服务器端两部分。客户端可以选择上传或下载文件,将文件分块并逐块发送到服务器,或者从服务器分块地接收文件。服务器端接收来自客户端的请求,根据…...
第二节 threejs简单案例
1. 创建3D场景 // 创建3D场景对象Scene const scene new THREE.Scene();// 更改场景背景颜色 scene.background new THREE.Color(#F5F5F5);2. 创建透视投影相机 // 实例化一个透视投影相机对象 const camera new THREE.PerspectiveCamera();相机位置 // 根据需要设置相机…...
PowerShell批量修改DNS域名解析
批量添加DNS A记录 $dnsServerName"" # DNS服务器的服务器名称,如果是在DNS服务器本机执行则可留空 $containerName"test.com" # 域名的后缀也就是DNS Zone Name $mydns[WMIClass]"ROOT\MicrosoftDNS:MicrosoftDNS_resourceRecord"…...
uniapp(uncloud) 使用生态开发接口详情3(新增产品分类,产品列表,新闻列表)
我的想法是有产品分类,产品列表,新闻咨询,新闻列表 项目中, uniCloud > database 目录下新建 sy_product_nav.schema.json // 代码如下 {"bsonType": "object","required": ["classname"],"permission": {"read&…...
XTU-OJ 1339-Interprime
题目描述 n是两个连续的奇素数的平均值,且n不是素数,那么我们称这样的数是"内部素数"。求区间[a,b]内"内部素数"的个数。比如,前5个"内部素数"是4,6,9,12,15。 输入 第一行是样例数T(1≤T≤1000)。 每个样例一…...
FPGA中的LUT查找表工作原理。
在RAM中填入1110,后续的不同AB组合选通对应RAM,Y输出对应RAM存储的值,实现上面逻辑表达式的功能。...
Python爬虫:制作一个属于自己的IP代理模块
前言 在Python爬虫过程中,为了避免被网站的反爬虫机制干扰,我们需要使用IP代理。所谓IP代理,就是通过修改网络请求中的IP地址,来达到隐藏真实IP地址的效果。本文将教你如何制作一个自己的IP代理模块,让你的爬虫更加稳…...
解决QT中文乱码
选中文本带有中文字符的文件,然后按如下点击 弹出对话框,选择当前操作系统的编码格式,选择Save with Encoding 中文字符前用u8进行标识...
GPIO基本原理
名词解释 高低电平:GPIO引脚电平范围:0V~3.3V(部分引脚可容忍5V)数据0就是0V,代表低电平;数据1就是3.3V,代表高电平; STM32是32位的单片机,所以内部寄存器也都是32位的…...
算法通过村第十五关-超大规模|青铜笔记|海量找数
文章目录 前言用4KB内存寻找重复数总结 前言 提示:并不是所有黑暗的地方,都需要光明。 --珍妮特温特森《句子不是唯一的水果》 在大部分算法中,默认给点给的数据量都是很小的,例如只有几个或者十几个元素,但是如果遇到…...
TCP、IP和HTTP的区别和联系
TCP(Transmission Control Protocol) TCP是一种面向连接的协议,负责数据的可靠性传输。它提供了错误检测和纠正、数据分段和重新组装、流量控制和拥塞控制等功能,最终确保数据可靠滴从一个端点传输到另一个端点。 TCP建立连接、传…...
【4】c++11新特性(稳定性和兼容性)—>final关键字
c中增加了final关键字来限制某个类不能被继承,或者某个虚函数不能被重写。如果使用final修饰函数,只能修饰虚函数,并且放在类或者函数的后面。 修饰函数 #include <iostream> using namespace std;class Base { public:virtual void t…...
23基于MATLAB的小波降噪,默认阈值消噪,强制消噪,给定软阈值消噪方法,数据直接替换后就可以跑。
基于MATLAB的小波降噪,默认阈值消噪,强制消噪,给定软阈值消噪方法,数据直接替换后就可以跑。 https://www.xiaohongshu.com/explore/652d57c600000...
蓝桥杯 常用STL (C++) 未完待续
动态数组 有些时候想开一个数组,但是却不知道应该开多大长度的数组合适,因为我们需要用到的数组可能会根据情况变动。 这时候我们就需要用到动态数组。所谓动态数组,也就是不定长数组,数组的长度是可以根据我们的需要动态改变的。…...
class id
在HTML和CSS中,"class" 和 "id" 是用于标识和定制元素的两种重要属性。 Class(类): "class" 属性用于标识一个或多个HTML元素,允许你为它们应用相同的样式规则。可以将相同的类应用于多个不同元素。…...
Qt (QInputDialog 、QMessageBox、QMessageBox)对话框实战
目录 一、QInputDialog 类(输入对话框) 二、QMessageBox 类(消息框) 三、QMessageBox 类(自定义消息框) 一、QInputDialog 类(输入对话框) QInputDialog 是一个提供输入对话框的 Qt 类。它允许用户输入文本,并提供给用户选择可用选项的选项列表。QInputDialog 可…...
Java 解析 cURL(bash) 命令
解析 cURL(bash) 命令 1. 主要用于解析从浏览器复制来的 cURL(bash)2. 废话不多说,都在🍻代码里了。参考资料 1. 主要用于解析从浏览器复制来的 cURL(bash) curl https://eva2.csdn.net/v3/06981375190026432f77c01bfca33e32/lts/…...
JDK21的虚拟线程是什么?和平台线程什么关系?
虚拟线程(Virtual Thread)是 JDK 而不是 OS 实现的轻量级线程(Lightweight Process,LWP),由 JVM 调度。许多虚拟线程共享同一个操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量。 在引入虚拟线程…...
Unity DOTS Component概述
最近DOTS终于发布了正式的版本, 我们来分享以下DOTS里面地几个关键概念,方便大家上手学习掌握Unity DOTS开发。 Unity DOTS 中Entity作为实体不直接去存放数据,而是将数据以一个个的组件为载体来存放起来。每个Entity会得到一些不同的ComponentData的组…...
element ui 下拉框 选择月份和天数
一、背景 目前做的管理系统项目,期望实现功能为:设置出账周期和出账日,考虑使用element ui下拉框实现功能 二、所用技术 vue2element ui 三、实现效果 四、具体代码 <template><popup-frame :title"批量设置出账日" …...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
