FTP 和 SFTP 介绍及 C/C++ 实现分析
1. FTP 协议概述
FTP(File Transfer Protocol)是一种用于在网络上进行文件传输的标准协议,诞生于 1971 年,是互联网上最早的应用层协议之一。它基于客户端 - 服务器模型,使用 TCP 作为传输层协议,默认通过 20 端口传输数据、21 端口传输控制命令。
FTP 工作模式分为主动模式(Active Mode)和被动模式(Passive Mode):
- 主动模式:客户端打开一个随机端口(>1023)向服务器的 21 端口发送连接请求,建立控制连接。当需要传输数据时,客户端在控制连接上发送 PORT 命令告知服务器自己的数据端口,服务器主动连接客户端的数据端口进行数据传输。
- 被动模式:客户端打开一个随机端口与服务器的 21 端口建立控制连接。当需要传输数据时,客户端发送 PASV 命令,服务器返回一个随机的数据端口,客户端主动连接该端口进行数据传输。
FTP 协议的主要缺点是缺乏安全性,所有数据(包括用户名、密码)都以明文形式传输,容易被窃听和中间人攻击。
2. SFTP 协议概述
SFTP(SSH File Transfer Protocol)是一种基于 SSH 协议的安全文件传输协议,它在 SSH 协议的基础上提供了文件传输功能。与 FTP 不同,SFTP 不依赖于独立的端口,而是使用 SSH 的 22 端口(或其他自定义 SSH 端口)进行所有通信,所有数据都经过加密处理。
SFTP 的主要优点:
- 安全性高:基于 SSH 协议,使用加密通道传输数据,防止数据被窃听和篡改。
- 集成认证:使用 SSH 的认证机制,无需额外的用户名密码管理。
- 防火墙友好:只需要开放 SSH 端口(通常是 22),简化了网络配置。
SFTP 与 FTP 的主要区别在于底层传输协议和安全性,SFTP 提供了更安全的文件传输环境,但性能可能略低于 FTP。
3. C/C++ 实现 FTP 客户端
在 C/C++ 中实现 FTP 客户端需要使用 socket 编程来与 FTP 服务器进行通信。以下是一个简单的 C++ 实现框架:
#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>class FTPClient {
private:int controlSocket;std::string server;int port;std::string username;std::string password;public:FTPClient(const std::string& server, int port = 21) : server(server), port(port), controlSocket(-1) {}~FTPClient() {disconnect();}bool connect() {controlSocket = socket(AF_INET, SOCK_STREAM, 0);if (controlSocket == -1) {std::cerr << "Failed to create socket" << std::endl;return false;}sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(port);serverAddr.sin_addr.s_addr = inet_addr(server.c_str());if (::connect(controlSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {std::cerr << "Failed to connect to server" << std::endl;close(controlSocket);controlSocket = -1;return false;}std::string response = receiveResponse();if (response.substr(0, 3) != "220") {std::cerr << "Unexpected response from server: " << response << std::endl;disconnect();return false;}return true;}void disconnect() {if (controlSocket != -1) {close(controlSocket);controlSocket = -1;}}bool login(const std::string& username, const std::string& password) {this->username = username;this->password = password;sendCommand("USER " + username);std::string response = receiveResponse();if (response.substr(0, 3) != "331") {std::cerr << "Invalid username: " << response << std::endl;return false;}sendCommand("PASS " + password);response = receiveResponse();if (response.substr(0, 3) != "230") {std::cerr << "Invalid password: " << response << std::endl;return false;}return true;}bool getFile(const std::string& remoteFile, const std::string& localFile) {// 实现文件下载逻辑// 1. 建立数据连接(主动或被动模式)// 2. 发送RETR命令// 3. 接收文件数据// 4. 关闭数据连接return true;}bool putFile(const std::string& localFile, const std::string& remoteFile) {// 实现文件上传逻辑// 1. 建立数据连接// 2. 发送STOR命令// 3. 发送文件数据// 4. 关闭数据连接return true;}private:void sendCommand(const std::string& command) {std::string cmd = command + "\r\n";send(controlSocket, cmd.c_str(), cmd.length(), 0);}std::string receiveResponse() {char buffer[1024];memset(buffer, 0, sizeof(buffer));int bytes = recv(controlSocket, buffer, sizeof(buffer) - 1, 0);if (bytes > 0) {return std::string(buffer);}return "";}
};
这个框架实现了 FTP 客户端的基本功能,包括连接、登录和文件传输的基本结构。实际实现中,还需要处理更多细节,如:
- 解析服务器响应代码
- 实现主动和被动模式的数据连接
- 处理文件传输的二进制和 ASCII 模式
- 实现目录操作(LIST、CWD 等)
- 添加错误处理和超时机制
4. C/C++ 实现 SFTP 客户端
在 C/C++ 中实现 SFTP 客户端通常需要使用第三方库,因为 SFTP 协议基于 SSH,实现复杂度较高。常用的库有:
- libssh2:一个功能齐全的 SSH2 协议实现库,支持 SFTP 功能。
- libssh:另一个 SSH 协议实现库,提供了更高级的 API。
以下是使用 libssh2 实现 SFTP 客户端的示例框架:
#include <iostream>
#include <string>
#include <libssh2.h>
#include <libssh2_sftp.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>class SFTPClient {
private:int socket;LIBSSH2_SESSION *session;LIBSSH2_SFTP *sftp_session;std::string server;int port;std::string username;std::string password;public:SFTPClient(const std::string& server, int port = 22) : server(server), port(port), socket(-1), session(nullptr), sftp_session(nullptr) {// 初始化libssh2库libssh2_init(0);}~SFTPClient() {disconnect();// 清理libssh2库libssh2_exit();}bool connect() {// 创建TCP连接socket = ::socket(AF_INET, SOCK_STREAM, 0);if (socket == -1) {std::cerr << "Failed to create socket" << std::endl;return false;}sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(port);serverAddr.sin_addr.s_addr = inet_addr(server.c_str());if (::connect(socket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {std::cerr << "Failed to connect to server" << std::endl;close(socket);socket = -1;return false;}// 初始化SSH会话session = libssh2_session_init();if (!session) {std::cerr << "Failed to initialize SSH session" << std::endl;disconnect();return false;}// 启动SSH连接if (libssh2_session_handshake(session, socket)) {std::cerr << "SSH handshake failed" << std::endl;disconnect();return false;}return true;}void disconnect() {// 关闭SFTP会话if (sftp_session) {libssh2_sftp_shutdown(sftp_session);sftp_session = nullptr;}// 关闭SSH会话if (session) {libssh2_session_disconnect(session, "Normal shutdown");libssh2_session_free(session);session = nullptr;}// 关闭TCP连接if (socket != -1) {close(socket);socket = -1;}}bool login(const std::string& username, const std::string& password) {this->username = username;this->password = password;// 进行密码认证if (libssh2_userauth_password(session, username.c_str(), password.c_str())) {std::cerr << "Authentication failed" << std::endl;return false;}// 初始化SFTP会话sftp_session = libssh2_sftp_init(session);if (!sftp_session) {std::cerr << "SFTP initialization failed" << std::endl;return false;}return true;}bool getFile(const std::string& remoteFile, const std::string& localFile) {// 打开远程文件LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_open(sftp_session, remoteFile.c_str(), LIBSSH2_FXF_READ, 0);if (!sftp_handle) {std::cerr << "Failed to open remote file" << std::endl;return false;}// 打开本地文件FILE *local_file = fopen(localFile.c_str(), "wb");if (!local_file) {std::cerr << "Failed to open local file" << std::endl;libssh2_sftp_close(sftp_handle);return false;}// 读取远程文件并写入本地文件char buffer[1024];int rc;while ((rc = libssh2_sftp_read(sftp_handle, buffer, sizeof(buffer))) > 0) {fwrite(buffer, 1, rc, local_file);}// 关闭文件fclose(local_file);libssh2_sftp_close(sftp_handle);return true;}bool putFile(const std::string& localFile, const std::string& remoteFile) {// 打开本地文件FILE *local_file = fopen(localFile.c_str(), "rb");if (!local_file) {std::cerr << "Failed to open local file" << std::endl;return false;}// 获取文件大小fseek(local_file, 0, SEEK_END);size_t file_size = ftell(local_file);rewind(local_file);// 打开远程文件LIBSSH2_SFTP_HANDLE *sftp_handle = libssh2_sftp_open(sftp_session, remoteFile.c_str(), LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR |LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH);if (!sftp_handle) {std::cerr << "Failed to open remote file" << std::endl;fclose(local_file);return false;}// 读取本地文件并写入远程文件char buffer[1024];size_t total_sent = 0;while (total_sent < file_size) {size_t to_send = (file_size - total_sent) < sizeof(buffer) ? (file_size - total_sent) : sizeof(buffer);size_t bytes_read = fread(buffer, 1, to_send, local_file);if (bytes_read <= 0) break;int rc = libssh2_sftp_write(sftp_handle, buffer, bytes_read);if (rc < 0) {std::cerr << "Error writing to remote file" << std::endl;break;}total_sent += bytes_read;}// 关闭文件fclose(local_file);libssh2_sftp_close(sftp_handle);return total_sent == file_size;}
};
5. 实现注意事项和优化建议
在实现 FTP 和 SFTP 客户端时,需要注意以下几点:
-
错误处理:网络操作容易出错,需要完善的错误处理机制,包括超时处理、连接断开重连等。
-
线程安全:在多线程环境中使用时,需要考虑线程安全问题,特别是 libssh2 库不是线程安全的,需要适当的同步机制。
-
性能优化:对于大文件传输,可以考虑使用异步 I/O 或多线程来提高性能。
-
安全性:在处理敏感信息(如密码)时,需要注意内存安全,避免信息泄露。
-
跨平台兼容性:确保代码在不同操作系统上都能正常工作,注意处理不同系统的路径分隔符差异等问题。
-
日志和调试:添加详细的日志记录功能,方便调试和问题排查。
总之,实现 FTP 和 SFTP 客户端是一个复杂的任务,特别是 SFTP 由于涉及加密和 SSH 协议,推荐使用成熟的第三方库来简化开发。在选择库时,需要考虑功能完整性、性能、稳定性和社区支持等因素。
相关文章:
FTP 和 SFTP 介绍及 C/C++ 实现分析
1. FTP 协议概述 FTP(File Transfer Protocol)是一种用于在网络上进行文件传输的标准协议,诞生于 1971 年,是互联网上最早的应用层协议之一。它基于客户端 - 服务器模型,使用 TCP 作为传输层协议,默认通过 …...

leetcode hot100刷题日记——36.最长连续序列
解答: 实际上在哈希表中存储不重复的数字。 然后遍历哈希表,找间隔,更新最大间隔。 class Solution { public:int longestConsecutive(vector<int>& nums) {unordered_set<int>hash;for(int num:nums){hash.insert(num);}in…...

CentOS7关闭防火墙、Linux开启关闭防火墙
文章目录 一、firewalld开启、关闭防火墙1、查看防火墙状态 一、firewalld开启、关闭防火墙 以下命令在linux系统CentOS7中操作开启关闭防火墙 # 查询防火墙状态 systemctl status firewalld.service # 开启防火墙 systemctl start firewalld.service # 开机自启动防火墙 syste…...

PyTorch——搭建小实战和Sequential的使用(7)
import torch from torch import nn from torch.nn import Conv2d, MaxPool2d, Flatten, Linearclass TY(nn.Module):def __init__(self):"""初始化TY卷积神经网络模型模型结构:3层卷积池化,2层全连接设计目标:处理32x32像素的…...
基于大模型的腔隙性脑梗塞风险预测及治疗方案研究
目录 一、引言 1.1 研究背景与意义 1.2 研究目的与方法 1.3 国内外研究现状 二、腔隙性脑梗塞概述 2.1 定义与分类 2.2 发病机制与病理生理过程 2.3 临床表现与诊断方法 三、大模型技术原理与应用现状 3.1 基本概念与技术架构 3.2 在医疗领域的应用案例与优势 3.3 …...

Python 开发效率秘籍:PyCharm、VS Code 与 Anaconda 配置与实战全解
目录 一、IDE(集成开发环境)是什么?二、Python IDE有哪些,哪款适合初学者?三、Visual Studio Code下载和安装教程3.1 VS Code下载和安装3.2 VS Code运行Python程序 四、PyCharm下载和安装教程4.1 PyCharm下载4.2 PyCharm安装4.3 运行PyCharm4.4 创建工程…...
[C]C语言日志系统宏技巧解析
代码解释:日志标签字符串化宏 这段代码定义了一个名为 _LOG_TAG 的宏,用于将 LOG_TAG_CONST 转换为字符串形式。这在日志系统中很常见,用于为不同模块添加标识前缀。 宏结构分析 #define _LOG_TAG STR(LOG_TAG_CON…...
自动驾驶系统研发系列—激光雷达感知延迟:自动驾驶安全的隐形隐患?
🌟🌟 欢迎来到我的技术小筑,一个专为技术探索者打造的交流空间。在这里,我们不仅分享代码的智慧,还探讨技术的深度与广度。无论您是资深开发者还是技术新手,这里都有一片属于您的天空。让我们在知识的海洋中一起航行,共同成长,探索技术的无限可能。 🚀 探索专栏:学…...
内网应用如何实现外网访问?无公网IP本地端口网址服务提供互联网连接
一、应用程序外网访问遇到的问题 在现实的工作场景中,在公司内网的服务器上有很多的应用系统,这些系统只能局限于在公司内部使用,而在外网却无法使用。 二、外网访问内网应用常见的解决方案 如何在外网使用这些系统呢?下面简单…...

大话软工笔记—组合要素1之要素
1. 要素来源 对象是要素的来源,要素是从对象分解而来的。可将对象分为优化类和非优化类,如下图所示。 对象分类图 2. 要素的概念 2.1 要素的定义 要素,是构成事物必不可少的因素,要素的集合体构成了对象。 2.2 要素的内容 要…...
oracle从表B更新拼接字段到表A
oracle中表A怎么从表B中追加相对应的编码到表A字段里, 在Oracle数据库中,如果你想从表B中获取数据并更新到表A的某个字段里,可以使用UPDATE语句结合子查询来实现。假设表A有一个字段叫做code,你希望根据某个键(比如id&…...

平台化 LIMS 系统架构 跨行业协同与资源共享的实现路径
在科技快速发展的今天,质检行业正面临着效率、合规和数据安全的多重挑战。新一代质检 LIMS 系统以智能化与平台化为核心,为实验室管理提供了全新的解决方案。 一、智能化:从数据采集到分析的全流程升级 传统质检流程中,人工数据录…...

RedisTemplate查询不到redis中的数据问题(序列化)
RedisTemplate查询不到redis中的数据问题(序列化) 一.问题描述 存入Redis中的值取出来却为null,问题根本原因就是RedisTemplate和StringRedisTemplate的序列化问题、代码示例: SpringBootTest class Redis02SpringbootApplicationTests {Autowiredprivate RedisTe…...
如何利用乐维网管进行IP管理
IP管理是网络管理中的关键环节,对于保障网络的正常运行、提升资源利用效率以及保障网络安全等方面都具有不可忽视的重要性。乐维网管在IP管理方面具有多种实用功能,以下从IP规划与分配、IP状态监测、IP冲突处理、IP审计与报表生成四个方面,介…...
unix/linux,sudo,其历史争议、兼容性、生态、未来展望
sudo作为一个广泛应用的系统工具,在其发展历程中,自然也伴随着一些讨论、挑战和对未来的展望。 一、 历史争议与讨论 (Historical Controversies and Discussions) 虽然sudo被广泛认为是成功的,但也存在一些历史上的讨论点或潜在的争议: 复杂性 vs. 简洁性 (sudoers语法)…...
git stash命令用法
git stash 是 Git 中一个非常有用的命令,它可以临时保存当前工作区的修改,让你可以切换到其他分支或者处理其他任务,而不需要提交这些还未完成的修改。 一、基本用法 1. 保存当前修改(包括暂存区和工作区的内容) git…...

SkyWalking如何实现跨线程Trace传递
一、概述 SkyWalking 的中构建 Trace 信息时会借助 ThreadLocal来存储一些上下文信息,当遇到跨线程的时候,如果 Trace 的上下文信息没有传递到新线程的ThreadLocal 中,那么链路就断开了。那么SkyWalking是如何解决这个问题的呢? …...
软件工程专业的本科生应该具备哪些技能
软件工程专业的本科生需要具备扎实的技术基础、良好的开发流程认知和一定的软技能,以适应软件开发行业的需求。以下从技术技能、开发流程与工具、软技能、实践能力等维度整理核心技能清单,供参考: 一、核心技术技能 1. 编程语言 - 必学基础语…...

使用 Spring Boot 3.3 和 JdbcTemplate 操作 MySQL 数据库
在现代的 Java 应用开发中,Spring Boot 提供了强大的工具来简化数据库操作。JdbcTemplate 是 Spring 提供的一个核心类,用于简化 JDBC 操作,减少样板代码。本文将介绍如何在 Spring Boot 3.3 项目中使用 JdbcTemplate 来操作 MySQL 数据库&am…...
CentOS 7 修改为静态 IP 地址完整指南
在企业网络环境中,服务器通常需要配置静态 IP 地址以确保网络连接的稳定性和可管理性。以下是使用 NetworkManager 工具在 CentOS 7 系统中将动态 IP 配置修改为静态 IP 的完整指南: 一、检查当前网络配置 查看网络连接状态: 使用 nmcli connection show 命令列出所有网络连…...
企业级高防CDN选型指南
#!/bin/bash # 高防CDN性能压测工具 # 使用方法:./stress_test.sh <防护域名>DOMAIN$1 TEST_IP$(dig short $DOMAIN | head -n1) # 获取CDN节点IPecho "[压力测试] 目标: $DOMAIN ($TEST_IP)" echo "----------------------------------"…...
Redis-6.2.9 cluster集群部署和扩容缩容
目录 1 操作系统信息和redis软件版本 2 redis集群架构 3 redis软件安装 4 cluster创建 6 Redis集群节点扩容 7 redis集群节点缩容节点 1 操作系统信息和redis软件版本 rootu24-redis-120:~# cat /etc/issue Ubuntu 24.04.2 LTS \n \l rootu24-redis-120:~# redis-server…...
Java求职者面试指南:DevOps技术栈深度解析
Java求职者面试指南:DevOps技术栈深度解析 一、基础概念问题 1. 请解释什么是Docker? Docker是一个开源的应用容器引擎,它允许开发者将应用程序及其依赖打包到一个可移植的容器中,然后在任何支持Docker的环境中运行。Docker的核…...
生产环境中安装和配置 Nginx 以部署 Flask 应用的详细指南
在生产环境中部署 Flask 应用时,Nginx 常被用作反向代理服务器,与 WSGI 服务器(如 Gunicorn)协同工作。Nginx 可以处理静态文件、提供 SSL/TLS 加密、实现负载均衡等功能。本文将详细介绍如何在 Ubuntu/Debian 系统上安装 Nginx&a…...

Axure高保真LayUI框架 V2.6.8元件库
点击下载《Axure高保真LayUI框架 V2.6.8元件库》 原型效果:https://axhub.im/ax9/bf36e6dd89bc4c9f/#g1 摘要 本文详细阐述了在 Axure 环境下打造的一套高度还原 LayUI 框架的组件元件集。通过对 LayUI 框架组件的深入剖析,结合 Axure 的强大功能&…...

通讯录实现(Linux+Cpp)
通讯录实现(LinuxCpp) 产品底层思考: 人员如何存储 -> 链表 (增删改 但是排序不适合) 文件存储 -> 人员数据的格式 name:xxx,phone:xxx 人员信息 -> 姓名、电话 引出2 name: xxx,phone: xxx,age: xxx,addr…...
K8S主机漏洞扫描时检测到kube-服务目标SSL证书已过期漏洞的一种永久性修复方法
1、背景 PaaS平台102xx、102xx端口检测到目标SSL证书已过期漏洞,分别对应kube-controller-manager证书、kube-scheduler证书。 2、系统版本 1.0、2.0版本均涉及。 k8s 1.19、1.23版本均涉及。 3、故障现象 PaaS平台部署1年以后,在主机漏洞扫描时&a…...

质检 LIMS 系统数据防护指南 三级等保认证与金融级加密方案设计
面对频发的数据泄露事件,企业亟需构建一套 “防得住、追得回、打得赢” 的防护体系。质检 LIMS 系统通过三级等保认证与金融级加密的结合,为这一目标提供了可行路径。 一、金融级加密:构建数据防护的 “铜墙铁壁” 金融级加密技术通过协议加密…...

Spring Boot 从Socket 到Netty网络编程(上):SOCKET 基本开发(BIO)与改进(NIO)
前言 无论是软件还是硬件的本质都是要解决IO问题(输入、输出),再说回网络编程本质上都是基于TCP/UP的开发,socket是在此基础上做的扩展与封装,而Netty又是对socket做的封装。本文旨在通过相关案例对socket进行探讨。 一…...

79. Word Search
题目描述 79. Word Search 回溯 代码一,使用used数组 class Solution {vector<pair<int,int>> directions{{0,1},{0,-1},{1,0},{-1,0}};vector<vector<bool>> used; public:bool exist(vector<vector<char>>& board, st…...