FTP服务器项目

news2025/1/12 17:53:11

文章目录

    • 1. 项目简介
    • 2. FTP协议和用到指令说明
      • 2.1 FTP协议详解
      • 2.2 指令说明
    • 3. FTP项目的类图分析
      • 3.1 UML
      • 3.2 工厂类XFtpFactory
        • XFtpFactory.h
        • XFtpFactory.cpp
      • 2.2 XFtpTask
        • XFtpTask.h
        • XFtpTask.cpp
      • 2.3 XFtpServerCMD
        • XFtpServerCMD.h
        • XFtpServerCMD.cpp
    • 4. 运行演示
      • FileZilla的配置
      • 运行效果

1. 项目简介

本项目实现一个FTP服务器,通过FTP客户端(如FileZilla)来获取文件列表、下载、上传、删除文件等功能。用到了libevent网络事件库管理socket的连接加入线程池来并发的处理请求,应用创建型模式-工厂模式来管理FTP客户端的请求命令码去注册对象。

  • Libevent的更多内容链接: libevent C++高并发网络编程

  • FTP协议的更多内容链接: FTP协议详解

  • 工厂模式的更多内容链接: C++设计模式 - 创建型模式之工厂模式

  • 项目完整代码git仓库:https://gitee.com/kakagitee1998/ftp_server.git

2. FTP协议和用到指令说明

2.1 FTP协议详解

FTP工作模式

  • 主动模式 PORT
    • 客户端首先和服务器的TCP 21端口建立连接,通过这个通道发送命令,客户端需要接收数据的时候在这个通道上发送
    • PORT命令PORT命令包含了客户端用什么端口接收数据
  • 被动模式 PASV
    • FTP服务器收到Pasv命令后,随机打开一个临时端口用于传送数据
  • 命令通道和数据通道

2.2 指令说明

连接成功 
	* 220 Welcome to XFtpServer (libevent)\r\n  

USER 用户登录
    * USER root\r\n
    * 230 Login successful.\r\n

PWD 获取当前目录
    * PWD\r\n
    * 257 "/" is current directory.

CWD 进入目录
    * CWD test\r\n
    * 250 Directory success changed.

CDUP 返回上传目录
    * CDUP\r\n
    * 250 Directory success changed  
    
PORT 客户端发送数据传送地址和端口
    * PORT 127,0,0,1,70,96\r\n
    * 200 PORT command successful.\r\n
    * 端口计算方法
        PORT n1,n2,n3,n4,n5,n6\r\n
        port = n5*256 + n6 
    
LIST 获取目录
    * LIST\r\n
    * 150 Here comes the directory listing.\r\n 450 file open failed.
    * 数据通道连接
        -rwxrwxrwx 1 root group 64463 Mar 14 09:53 101.jpg\r\n
    	ls -l
    * 226 Transfer complete\r\n
    * 关闭数据通道
    
RETR 下载文件
    * RETR filepath\r\n
    * 150 Transfer start.\r\n 450 file open failed.
    * 数据通道连接 传送文件数据给客户端
    * 226 Transfer complete\r\n
    * 关闭数据通道
            
STOR 上传文件
    * STOR filepath\r\n
    * 125 file OK.\r\n
    * 数据通道 读取上传的文件 客户端发送结束会主动关闭数据通道
    * 226 Transfer complete\r\n 

3. FTP项目的类图分析

3.1 UML

在这里插入图片描述

3.2 工厂类XFtpFactory

工厂类XFtpFactory作用是封装对象的创建,分离对象的创建和操作过程,用于批量管理对象的创建过程,便于程序的维护和扩展。

  • GetInstance():通过单例模式创建返回唯一对象;
  • CreateTask():创建XFtpServerCMD对象并将XFtp(USER、PWD、LIST、CWD、CDUP、PORT、RETR、STOR)等指令处理对象注册到工厂类对象中,后续对指令的操作只需要通过命令的名字从容器中取出对象,简化了操作过程,只需要知道令的名字即可。

XFtpFactory.h

#pragma once
#include "XTask.h"

class XFtpFactory
{
public:
    //单例模式创建返回唯一对象
    static XFtpFactory* GetInstance();

    XTask* CreateTask();

private:
    //将构造函数的访问属性设置为 private
    //将构造函数构造声明成私有不使用
    //声明成私有不使用
    XFtpFactory(){}                                 //无参构造
    XFtpFactory(const XFtpFactory&);                //拷贝构造
    XFtpFactory& operator= (const XFtpFactory&);    //赋值运算符重载

    //FTP工厂对象
    static XFtpFactory* pInstance;
};

XFtpFactory.cpp

#include <iostream>
#include "XFtpFactory.h"
#include "XFtpServerCMD.h"
#include "XFtpUSER.h"
#include "XFtpPORT.h"
#include "XFtpLIST.h"
#include "XFtpRETR.h"
#include "XFtpSTOR.h"

using namespace std;

//静态成员变量类外初始化
XFtpFactory* XFtpFactory::pInstance = NULL;

/*
* 函数名: XFtpFactory::GetInstance
* 作用:   单例模式创建返回唯一对象
*/
XFtpFactory* XFtpFactory::GetInstance()
{
    //当需要使用对象时,访问instance 的值
    //空值:创建对象,并用instance 标记
    //非空值: 返回instance 标记的对象
    if( pInstance == NULL )
    {
        pInstance = new XFtpFactory();
    }
    
    return pInstance;
}

XTask* XFtpFactory::CreateTask()
{
    XFtpServerCMD* x = new XFtpServerCMD();

    //注册ftp消息处理对象
    x->Reg("USER", new XFtpUSER());

    XFtpLIST* list = new XFtpLIST();
    x->Reg("PWD", list);
    x->Reg("LIST", list);
    x->Reg("CWD", list);
    x->Reg("CDUP", list);
    
    x->Reg("PORT", new XFtpPORT());

    x->Reg("RETR", new XFtpRETR());
	x->Reg("STOR", new XFtpSTOR());

 	return x;
}

2.2 XFtpTask

继承XTask任务类,其他命令任务处理都继承它,并且实现他的虚函数。

XFtpTask.h

#pragma once
#include "XTask.h"
#include <string>

class XFtpTask : public XTask
{
public:
    std::string curDir  = "/home/kaka/linux/libevent/code/14.tev_ftp_server";
    std::string rootDir = "";

    /*PORT 数据通道的IP和端口*/
    std::string ip = "";
    int port = 0;

    //命令通道
	XFtpTask* cmdTask = 0;
    
    //解析协议
    virtual void Parse(std::string type, std::string msg){}

    //回复cmd消息
    void ResCMD(std::string msg);

    //用来发送建立了连接的数据通道
    void Send(std::string data);
    void Send(const char* data, int datasize);

    //连接数据通道
    void ConnectPORT();
    void Close();

    virtual void Read(struct bufferevent* bev) {}
    virtual void Write(struct bufferevent* bev) {}
    virtual void Event(struct bufferevent* bev, short what) {}
    void SetCallback(struct bufferevent* bev);
    bool Init() { return true; } //继承于基类的纯虚函数需要实现
protected:
    static void ReadCB(struct bufferevent* bev, void* arg);
    static void WriteCB(struct bufferevent* bev, void* arg);
    static void EventCB(struct bufferevent* bev, short what, void* arg);

    //命令bev
    struct bufferevent* bev = NULL;
    FILE* fp = 0;
};

XFtpTask.cpp

#include <iostream>
#include "XFtpTask.h"
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <string>
#include <string.h>

using namespace std;

//用来发送建立了连接的数据通道
void XFtpTask::Send(std::string data)
{
    Send(data.c_str(), data.size());
}
void XFtpTask::Send(const char* data, int datasize)
{
    if(!bev)return;

    bufferevent_write(bev, data, datasize);
}


//连接数据通道
void XFtpTask::ConnectPORT()
{
    if(ip.empty() || port<=0 || !base)
    {
        cout << "ConnectPORT failed ip or port or base is null" << endl;
		return;
    }
    Close();

    bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
    sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
    evutil_inet_pton(AF_INET, ip.c_str(), &sin.sin_addr.s_addr);

    //设置回调和权限
	SetCallback(bev);

    //添加超时 
	timeval rt = { 60,0 };
	bufferevent_set_timeouts(bev, &rt, 0);
	bufferevent_socket_connect(bev, (sockaddr*)&sin, sizeof(sin));
}

void XFtpTask::Close()
{
    if(bev)
    {
        bufferevent_free(bev);
        bev = NULL;
    }

    if(fp)
    {
        fclose(fp);
        fp = NULL;
    }
}


//回复cmd消息
void XFtpTask::ResCMD(std::string msg)
{
    if(!cmdTask || !cmdTask->bev)return;
    cout << "ResCMD:" << msg << endl;
    if(msg[msg.size() - 1] != '\n')
        msg += "\r\n";
    bufferevent_write(cmdTask->bev, msg.c_str(), msg.size());
}

void XFtpTask::SetCallback(struct bufferevent* bev)
{
    bufferevent_setcb(bev, ReadCB, WriteCB ,EventCB, this);
    bufferevent_enable(bev, EV_READ | EV_WRITE);
}

void XFtpTask::ReadCB(struct bufferevent* bev, void* arg)
{
    XFtpTask *t = (XFtpTask*)arg;
    t->Read(bev);
}

void XFtpTask::WriteCB(struct bufferevent* bev, void* arg)
{
    XFtpTask *t = (XFtpTask*)arg;
    t->Write(bev);
}

void XFtpTask::EventCB(struct bufferevent* bev, short what, void* arg)
{
    XFtpTask *t = (XFtpTask*)arg;
    t->Event(bev, what);
}

2.3 XFtpServerCMD

接收客户端的命令分发,根据命令从工厂中得到对应的处理对象,std::map< std::string, XFtpTask*> calls,执行Parse()处理相应FTP客户端。

XFtpServerCMD.h

#pragma once

#include "XFtpTask.h"
#include <map>

class XFtpServerCMD : public XFtpTask
{
public:
    //初始化任务
    virtual bool Init();

    virtual void Read(struct bufferevent* bev);
    virtual void Write(struct bufferevent* bev);
    virtual void Event(struct bufferevent* bev, short what);

    //注册命令处理对象 不需要考虑线程安全,调用时还未分发到线程
    void Reg(std::string , XFtpTask* call);

    XFtpServerCMD();
    ~XFtpServerCMD();

private:
    //注册命令集合
    std::map<std::string, XFtpTask*> calls;

    //用来做空间清理
	std::map< XFtpTask*, int>calls_del;
};

XFtpServerCMD.cpp

#include "XFtpServerCMD.h"
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <iostream>
#include <string.h>
#include <string>

using namespace std;

//注册命令处理对象 不需要考虑线程安全,调用时还未分发到线程
void XFtpServerCMD::Reg(std::string cmd, XFtpTask* call)
{
    if (!call)
	{
		cout << "XFtpServerCMD::Reg call is null " << endl;
		return;
	}
	if (cmd.empty())
	{
		cout << "XFtpServerCMD::Reg cmd is null " << endl;
		return;
	}

	//已经注册的是否覆盖 不覆盖,提示错误
    if(calls.find(cmd) != calls.end())
    {   
        cout << cmd << " is alreay register" << endl;
		return;
    }

    calls[cmd] = call;
    //用来做空间清理
	calls_del[call] = 0;
}


/*
* 函数名: Read
* 作用:   读事件回调函数
*/
void XFtpServerCMD::Read(struct bufferevent *bev)
{
    char data[1024] = {0};

    for (;;)
    {
        int len = bufferevent_read(bev, data, sizeof(data)-1);
        if(len <= 0)break;
        data[len] = '\0';
        cout << "Recv CMD:"<<data;
		//分发到处理对象

        //分析出类型 USER anonymous'
        string type = "";
        for (int i = 0; i < len; i++)
        {
            if(data[i] == ' ' || data[i] == '\r')
                break;

            type += data[i];
        }
        cout << "type is [" << type<<"]" << endl;
        
        //查找组成的命令
        if(calls.find(type) != calls.end()) //查找到
        {   
            XFtpTask* t = calls[type];
            t->cmdTask = this;  //用来处理回复命令和目录
            t->ip = ip;
            t->port = port;
            t->base = base;
            t->Parse(type, data);
            if(type == "PORT")
            {
                ip = t->ip;
                port = t->port;
            }
        }
        else
        {
            string msg = "200 OK\r\n";
			bufferevent_write(bev, msg.c_str(), msg.size());
        }
    }
}

/*
* 函数名: Write
* 作用:   写事件回调函数
*/
void XFtpServerCMD::Write(struct bufferevent *bev)
{

}

/*
* 函数名: Event
* 作用:   超时事件回调函数
* 解释:   客户端超时未发请求,断开连接退出任务
*/
void XFtpServerCMD::Event(struct bufferevent *bev, short what)
{
    //如果对方网络断掉,或者机器死机有可能收不到BEV_EVENT_EOF数据
    if(what & (BEV_EVENT_EOF | BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT))
    {
        cout << "BEV_EVENT_EOF | BEV_EVENT_ERROR |BEV_EVENT_TIMEOUT" << endl;
		bufferevent_free(bev);
		delete this;
    }
}


/*
* 函数名: XFtpServerCMD::Init
* 作用:   初始化任务
* 解释:   初始化任务,注册当前socket的读事件和超时事件,绑定回调函数。
*/
bool XFtpServerCMD::Init()
{
    cout << "XFtpServerCMD::Init() sock:" << sock << endl;
	//监听socket bufferevent
	// base socket
    bufferevent* bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
    if (!bev)
	{
		delete this;
		return false;
	}

    this->bev = bev;
    this->SetCallback(bev);

    //添加超时
    timeval rt = {10, 0};       //10秒
    bufferevent_set_timeouts(bev, &rt, 0);  //设置读超时回调函数

    //FTP连接成功,首先回欢迎消息
    string msg = "220 Welcome to libevent XFtpServer\r\n";
    bufferevent_write(bev, msg.c_str(), msg.size());

	return true;
}

XFtpServerCMD::XFtpServerCMD()
{

}

XFtpServerCMD::~XFtpServerCMD()
{
    Close();
	for (auto ptr = calls_del.begin(); ptr != calls_del.end(); ptr++)
	{
		ptr->first->Close();
		delete ptr->first;
	}
}

4. 运行演示

FileZilla的配置

协议选择:FTP-文件传输协议
登录类型:匿名
传输模式:主动
字符集:UTF8

在这里插入图片描述

运行效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/634553.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数字逻辑期末必刷卷(基础卷)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 &#x1f4a1;一、填空题&#xff08;每空1分&#xff0c;共20分&#xff09;&#x1f4a1;二、单项选择题&#xff08;每小题2分&#xff0c;共20分&#xff09;&a…

第七章 Linux实际操作——组管理和权限管理

第七章 Linux实际操作——组管理和权限管理 7.1 Linux组基本介绍7.2 文件、目录 所有者7.2.1 查看文件的所有者7.2.2 修改文件所有者 7.3 组的创建7.3.1 基本指令7.3.2 应用实例 7.4 文件、目录所在组7.4.1 查看文件、目录所在组7.4.2 修改文件、目录所在组 7.5 其他组7.6 权限…

观澜南林輋旧改回迁房--周边巨量旧改,未来区政府核心商圈。

项目亮点 观澜福城街道办旧改最集中的区域&#xff0c;且地铁4号的茜坑站就在门口&#xff01;未来一区域成为龙华区政府的中心地段。本项目拆迁约10万&#xff0c;主打高端商业综合体&#xff0c;项目规划27班九年一贯性学校&#xff0c;约4万多平用于建设公共设施、绿地。 …

ROS学习——通信机制(服务通信)

2.2.3 服务通信自定义srv调用A(C) Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 068服务通信(C)3_客户端优化_Chapter2-ROS通信机制_哔哩哔哩_bilibili 一、理论模型 服务通信也是ROS中一种极其常用的通信模式&#xff0c;服务通信是基于请求响应模式的&#xf…

【Java|多线程与高并发】volatile关键字和内存可见性问题

文章目录 1.前言2. 编译器优化带来的内存可见性问题3. 使用volatile保证内存可见性5.volatile不能保证原子性以JMM的角度看待volatile总结 1.前言 synchronized和volatile都是Java多线程中很重要的关键字&#xff0c;但它们的作用和使用场景有所不同。 synchronized关键字可以…

Linux之文件打包和解压缩

任务描述 有时&#xff0c;我们会在Linux系统中将多个文件打包成一个单独的文件&#xff0c;通过本关的学习&#xff0c;我们将学会如何在Linux系统中将多个文件/目录打包生成一个文件。 本关任务&#xff1a;使用tar命令完成文件和目录的打包操作。 相关知识 tar&#xff…

验证断言(立即断言并行断言)

目录 1.何为断言 2.断言的作用&#xff1a; 3.断言的种类 3.1立即断言 3.2并发断言 4.断言层次结构 4.1 sequence 序列 4.2 property 序列 5.sequence和property的异同 6.补充知识点&#xff08;assert/cover/assume&#xff09; 7.写在后边 1.何为断言 断言主要…

网络知识点之-FTP协议

FTP协议指文件传输协议&#xff08;File Transfer Protocol&#xff0c;FTP&#xff09;&#xff0c;是用于在网络上进行文件传输的一套标准协议&#xff0c;它工作在 OSI 模型的第七层&#xff0c; TCP 模型的第四层&#xff0c; 即应用层&#xff0c; 使用 TCP 传输而不是 UD…

第一节 初识C语言

第一节 初识C语言 目录 一&#xff0e; 什么是C语言二&#xff0e; 第一个C语言程序三&#xff0e; 数据类型四&#xff0e; 变量与常量五&#xff0e; 未完待续 本章重点&#xff1a; 什么是C语言第一个C语言程序数据类型变量、常量字符串转义字符注释选择语句循环语句函数数组…

【LeetCode】每日一题 -- 1171. 从链表中删去总和值为零的连续节点 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/remove-zero-sum-consecutive-nodes-from-linked-list/ 1. 题解&#xff08;1171. 从链表中删去总和值为零的连续节点&#xff09; 2021年字节二面真题 1.1 暴力解法&#xff1a;穷举 时间复杂度 O(n2)&#xff0c;空间复杂…

Python系列之面向对象编程

目录 一、面向对象编程 1.1 面向对象三大特征 1.2 什么是对象 二、类(class)和实例(instance) 2.1 类的构成 2.2 创建类 2.3 创建实例对象和访问属性 2.4 Python内置类属性 2.5 类属性与方法 三、类的继承 3.1 方法重写 四、多态 一、面向对象编程 1.1 面向对象三大…

抖音短视频矩阵系统-源码-系统搭建

目录 1. 短视频AI智能创作 2. 托管式账号管理: 3. 数据分析 4. 智能营销获客 开发流程 抖音账号矩阵系统开发&#xff0c;抖音账号矩阵系统源码搭建&#xff0c;抖音账号技术系统源码部署 抖音矩阵系统专注于为短视频私域运营达人或企业提供一站式赋能服务平台。具体包括智…

小议CSDN周赛57期 - 凑数

本期周赛几乎忘记参加&#xff0c;在最后几分钟的时候上来看了看。那些选择判断一通乱选&#xff0c;填空题也已经被吐槽得差不多了&#xff0c;这里不多说&#xff0c;只说我对第一道编程题的看法&#xff08;吐槽&#xff09;。因为 C 站的机制是&#xff0c;即使它错了&…

彻底理解HTTPS加密原理

目录 1.为什么需要加密&#xff1f; 2.什么是对称加密&#xff1f; 3.什么是非对称加密&#xff1f; 4.非对称加密对称加密&#xff1f; 5.数字证书 6.数字签名 相信大家对于HTTP与HTTPS的区别都有了解&#xff0c;那么对于HTTPS的加密过程你是否知道呢&#xff1f; 对称…

单片机内存管理

单片机内存管理 1、随机存储器 RAM是随机存储器&#xff0c;读写速度快&#xff0c;但掉电以后数据会丢失。它分为SRAM(静态RAM)和DRAM(动态RAM)。SRAM无需刷新就可以保存数据&#xff1b;DRAM需要不断刷新才可以保存数据。在CPU内部的RAM&#xff0c;就叫内部RAM&#xff0c…

算法模板(3):搜索(4):高等图论

高等图论 有向图的强连通分量 相关概念 强连通分量&#xff1a;Strongly Connected Component (SCC).对于一个有向图顶点的子集 S S S&#xff0c;如果在 S S S 内任取两个顶点 u u u 和 v v v&#xff0c;都能找到一条 u u u 到 v v v 的路径&#xff0c;那么称 S S…

JVM零基础到高级实战之Java程序员不可不知的对象创建底层步骤细节

JVM零基础到高级实战之Java程序员不可不知的对象创建底层步骤细节 JVM零基础到高级实战之Java程序员不可不知的对象创建底层步骤细节 文章目录 JVM零基础到高级实战之Java程序员不可不知的对象创建底层步骤细节前言Java对象创建的流程步骤包括哪些&#xff1f;总结 前言 JVM零…

【云原生 | 53】Docker三剑客之Docker Compose应用案例一:Web负载均衡

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…

基于Echarts构建停车场数据可视化大屏(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

【部署LVS-DR 群集】

目录 一、DR模式 LVS负载均衡群集1、数据包流向分析2、DR 模式的特点 二、DR模式 LVS负载均衡群集部署1、1.配置负载调度器&#xff08;192.168.80.30&#xff09;&#xff08;1&#xff09;配置虚拟 IP 地址&#xff08;VIP&#xff1a;192.168.102.188&#xff09;&#xff0…