网络上的文件传输功能也是很有必要实现一下的,网络传输文件的过程通常分为客户端和服务器端两部分。客户端可以选择上传或下载文件,将文件分块并逐块发送到服务器,或者从服务器分块地接收文件。服务器端接收来自客户端的请求,根据请求类型执行对应的操作,并根据发送的文件名或其他标识来确定要传输的文件。

在实现文件传输之前,需要先打开要传输的文件,并获取文件的大小信息,也可以通过其他方式获取文件的信息。在客户端和服务器端都准备就绪后,可以通过套接字来发送文件数据。在传输文件的过程中,可以将文件分解为若干个数据包进行传输,以减少数据传输中的丢包或传输错误。每个数据包的长度可以根据实际情况进行选择,通常选择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与客户端进行通信。在与客户端通信的过程中,可以通过sendrecv函数进行数据的传输,实现文件的上传和下载功能。此处的代码调用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 高效文件传输的更多相关文章

  1. 洗礼灵魂,修炼python(86)--全栈项目实战篇(12)—— 利用socket实现文件传输/并发式聊天

    由于本篇博文的项目都很简单,所以本次开个特例,本次解析两个项目,但是都很简单的 项目一:用socket实现文件传输 本项目很简单,作为小项目的预热的,前面刚学完socket,这里马上又利用socket ...

  2. python socket实现文件传输(防粘包)

    1.文件传输的要点: 采用iterator(迭代器对象)迭代读取,提高读取以及存取效率: 通过for line in file_handles逐行conn.send(): 2.socket粘包问题: ...

  3. Java简单文件传输 socket简单文件传输示例

    服务器端代码: import java.io.*; import java.net.*; /** * Created with IntelliJ IDEA. * User: HYY * Date: 1 ...

  4. python 3.x 学习笔记14 (socket_ssh and socket_文件传输)

    ssh服务端 import socket,os server = socket.socket() server.bind(('localhost',6666)) server.listen() con ...

  5. Java开发笔记(一百一十五)使用Socket开展文件传输

    前面介绍了怎样通过Socket在客户端与服务端之间传输文本,当然Socket也支持在客户端与服务端之间传输文件,因为文件本身就是通过I/O流实现读写操作的,所以在套接字的输入输出流中传输文件真是再合适 ...

  6. socket实现文件传输

    server:===========================================import socketimport structimport jsonsk = socket.s ...

  7. Java学习笔记——Socket实现文件传输

    我越是逃离,却越是靠近你. 我越是背过脸,却越是看见你. 我从你开始, 我在你结束. 需求:实现局域网下socket传输文件. 客户端步骤: 1.建立与服务器的连接 2.创建client输出流 3.创 ...

  8. JAVA Socket:文件传输

    客户端:读取文件(FileInputStream),发送文件(OutputStream) 服务器端:接收文件(InputStream),写文件(FileOutputStream) 客户端代码: pac ...

  9. windows环境下使用C++&Socket实现文件传输

    server #include <stdio.h> #include <iostream> #include <cstring> #include <fstr ...

  10. Linux网络编程:socket文件传输范例

    基于TCP流协议的socket网络文件传输Demo: 实现:C语言功能:文件传输(可以传任何格式的文件) /********************************************** ...

随机推荐

  1. Centos7 升级 Kubernetes(k8s) 集群

    目录 一.系统环境 二.前言 三.Kubernetes(k8s) 集群升级简介 四.升级master主节点 4.1 升级kubeadm 4.2 升级各个组件 4.3 升级 kubelet 和 kube ...

  2. fdisk 命令 创建分区 实现扩容

    fdisk 命令 创建分区 实现扩容 Linux fdisk命令简介 Linux fdisk 是一个创建和维护分区表的程序,它兼容 DOS 类型的分区表.BSD 或者 SUN 类型的磁盘列表. 菜单操 ...

  3. 【xx-job】 定时任务调度

    XXL-JOB XXL-JOB是一个分布式任务调度平台,其核心设计目标是开发迅速.学习简单.轻量级.易扩展. 现已开放源代码并接入多家公司线上产品线,开箱即用. 一.任务调度中心 1.1 下载XXL- ...

  4. 前端uni-app自定义精美全端复制文本插件,支持全端文本复制插件 可设置复制按钮颜色

    随着技术的发展,开发的复杂度也越来越高,传统开发方式将一个系统做成了整块应用,经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改,造成牵一发而动全身. 通过组件化开发,可以有 ...

  5. 数据库连接池之c3p0-0.9.1.2,线上偶发APPARENT DEADLOCK,如何解?

    前言 本篇其实是承接前面两篇的,都是讲定位线上的c3p0数据库连接池,发生连接泄露的问题. 第二篇讲到,可以配置两个参数,来找出是哪里的代码借了连接后没有归还.但是,在我这边的情况是,对于没有归还的连 ...

  6. linux内核编译体验篇(一)

    文章目录 一. 准备环境 二. 获取内核源码 三. 交叉编译工具链的配置 1. 博友们常用安装方法链接 2. 公司常用的交叉工具链使用方法 四. 内核解压以及如何打补丁 五. 内核基本配置 1. 编译 ...

  7. Redis核心技术与实践 01 | 基本架构:一个键值数据库包含什么?

    原文地址:https://time.geekbang.org/column/article/268262 个人博客地址:http://njpkhuan.cn/archives/redis-he-xin ...

  8. 在Java项目中使用redisson实现分布式锁

    Redisson自定义注解实现分布式锁 在Java项目中使用Redission自定义注解实现分布式锁: 添加Redission依赖项:在项目的pom.xml中添加Redission依赖项: <d ...

  9. UG NX实现叉车运输货物功能遇见的问题

    在前一段时间编写模拟叉车运输功能时遇到,货物无法跟随的问题(如下动图) 后面发现是速度太快的原因导致货物没有跟着动,类似于抽桌布的感觉 解决办法有两种:第一种解决办法很简单就是把速度降低到不超过  2 ...

  10. WPF自定义控件之消息提示

    创建消息提示控件 internal class Message : ContentControl { public int Time { get; set; } [Bindable(true)] pu ...