应用层协议实现系列(三)——FTP服务器之设计与实现

本文介绍如何实现FTP服务器,采用多进程机制处理每个连接的命令端口和数据端口。讲解FTP协议的通信流程,包括状态码、命令格式、二进制与ASCII码传输模式、Passive和Active模式的区别。通过示例代码解析关键步骤,如处理PORT命令时的字节顺序转换,并提供完整源码供读者参考。
摘要由CSDN通过智能技术生成

在实现了HTTP服务器之后,本人打算再实现一个FTP服务器。由于FTP协议与HTTP一样都位于应用层,所以实现原理也类似。在这里把实现的原理和源码分享给大家。

首先需要明确的是FTP协议中涉及命令端口和数据端口,即每个客户端通过命令端口向服务器发送命令(切换目录、删除文件等),通过数据端口从服务器接收数据(目录列表、下载上传文件等)。这就要求对每个连接都必须同时维护两个端口,如果使用类似于上一篇文章中的多路IO就会复杂很多,因此本文采用了类似Apache的多进程机制,即对每个连接创建一个单独的进程进行管理。

接下来简要说明一下FTP协议的通信流程,Ftp服务器向客户端发送的消息主要由两部分组成,第一部分是状态码(与HTTP类似),第二部分是具体内容(可以为空),两部分之间以空格分隔,如“220 TS FTP Server ready”就告诉了客户端已经连接上了服务器;客户端向服务器发送的命令也由两部分组成,第一部分是命令字符串,第二部分是具体内容(可以为空),两部分之间也以空格分隔,如“USER anonymous”就指定了登录FTP服务器的用户名。以一个登录FTP服务器并获取目录列表的流程为例:

220 TS FTP Server ready...
USER anonymous
331 Password required for anonymous
PASS chrome@example.com
530 Not logged in,password error.
QUIT
221 Goodbye
USER zhaoxy
331 Password required for zhaoxy
PASS 123
230 User zhaoxy logged in
SYST
215 UNIX Type: L8
PWD
257 "/" is current directory.
TYPE I
200 Type set to I
PASV
227 Entering Passive Mode (127,0,0,1,212,54)
SIZE /
550 File not found
PASV
227 Entering Passive Mode (127,0,0,1,212,56)
CWD /
250 CWD command successful. "/" is current directory.
LIST -l
150 Opening data channel for directory list.
16877 8 501 20        272 4 8 114 .
16877 29 501 20        986 4 8 114 ..
33188 1 501 20       6148 3 28 114 .DS_Store
16877 4 501 20        136 2 27 114 css
33279 1 501 20  129639543 6 14 113 haha.pdf
16877 11 501 20        374 2 27 114 images
33261 1 501 20      11930 3 9 114 index.html
16877 6 501 20        204 2 28 114 js
226 Transfer ok.
QUIT
221 Goodbye
在一个客户端连接到服务器后,首先服务器要向客户端发送欢迎信息220,客户端依此向服务器发送用户名和密码,服务器校验之后如果失败则返回530,成功则返回230。一般所有的客户端第一次连接服务器都会尝试用匿名用户进行登录,登录失败再向用户询问用户名和密码。接下来,客户端会与服务器确认文件系统的类型,查询当前目录以及设定传输的数据格式。

FTP协议中主要有两种格式,二进制和ASCII码,两种格式的主要区别在于换行,二进制格式不会对数据进行任何处理,而ASCII码格式会将回车换行转换为本机的回车字符,比如Unix下是\n,Windows下是\r\n,Mac下是\r。一般图片和执行文件必须用二进制格式,CGI脚本和普通HTML文件必须用ASCII码格式。

在确定了传输格式之后,客户端会设定传输模式,Passive被动模式或Active主动模式。在被动模式下,服务器会再创建一个套接字绑定到一个空闲端口上并开始监听,同时将本机ip和端口号(h1,h2,h3,h4,p1,p2,其中p1*256+p2等于端口号)发送到客户端。当之后需要传输数据的时候,服务器会通过150状态码通知客户端,客户端收到之后会连接到之前指定的端口并等待数据。传输完成之后,服务器会发送226状态码告诉客户端传输成功。如果客户端不需要保持长连接的话,此时可以向服务器发送QUIT命令断开连接。在主动模式下,流程与被动模式类似,只是套接字由客户端创建并监听,服务器连接到客户端的端口上进行数据传输。

以下是main函数中的代码:

#include <iostream>
#include "define.h"
#include "CFtpHandler.h"
#include <sys/types.h>
#include <sys/socket.h>

int main(int argc, const char * argv[])
{
    int port = 2100;
    int listenFd = startup(port);
    //ignore SIGCHLD signal, which created by child process when exit, to avoid zombie process
    signal(SIGCHLD,SIG_IGN);
    while (1) {
        int newFd = accept(listenFd, (struct socka
  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值