| 类别 | 内容 |
|---|---|
| 关键词 | Sylixos FTP Vxworks |
| 摘 要 | Vxworks组件FTP移植和使用 |
- 1. 适用范围
- 2. 原理概述
- 2.1 FTP原理
- 3. 准备工作
- 3.1 环境准备
- 3.2 资源准备
- 4. 技术实现
- 4.1 base工程和app工程设置
- 4.2 ftpLib移植
- 5. 如何使用FTP
- 6. 总结
本文适用对FTP有使用需求的工程师,讲述从FTP移植到使用。
2. 原理概述文件传输协议(File Transfer Protocol,FTP)是用于在网络上进行文件传输的一套标准协议,它工作在 OSI 模型的第七层, TCP 模型的第四层, 即应用层, 使用 TCP 传输而不是 UDP, 客户在和服务器建立连接前要经过一个“三次握手”的过程, 保证客户与服务器之间的连接是可靠的, 而且是面向连接, 为数据传输提供可靠保证。
FTP允许用户以文件操作的方式(如文件的增、删、改、查、传送等)与另一主机相互通信。然而, 用户并不真正登录到自己想要存取的计算机上面而成为完全用户, 可用FTP程序访问远程资源, 实现用户往返传输文件、目录管理以及访问电子邮件等等, 即使双方计算机可能配有不同的操作系统和文件存储方式。
FTP主要用来进行文件传输,包括命令信道和数据信道,命令信道用来交互命令控制,数据信道用来传输文件数据。
首先客户端需要进行登录FTP服务器,TCP建立连接,通过命令信道发送登录命令,传输数据时,通过命令信道交互数据端口号,彼此建立数据连接,数据可通过数据连接进行传输。
文件传输可选文件类型:ASCII码文件(默认的)/ 图像文件类型(二进制的)/ 本地文件类型(用于在具有不同字节大小主机间传送二进制数据)
与服务器连接分为两种模式:
主动 FTP :
命令连接:客户端 >1024 端口 → 服务器 21 端口
数据连接:客户端 >1024 端口 ← 服务器 20 端口
被动 FTP :
命令连接:客户端 >1024 端口 → 服务器 21 端口
数据连接:客户端 >1024 端口 ← 服务器 >1024 端口
PORT(主动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路。 当需要传送数据时, 客户端在命令链路上用PORT命令告诉服务器:“我打开了***X端口,你过来连接我”。于是服务器从20端口向客户端的***X端口发送连接请求,建立 一条数据链路来传送数据。
PASV(被动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路。 当需要传送数据时, 服务器在命令链路上用PASV命令告诉客户端:“我打开了***X端口,你过来连接我”。于是客户端向服务器的***X端口发送连接请求,建立一条数据链 路来传送数据。
早先所有客户端都使用主动模式,而且工作的很好,而现在因为客户端防火墙的存在,将会关闭一些端口,这样主动模式将会失败。在这种情况下就要使用被动模式,但是一些端口也可能被服务器的防火墙封掉。不过因为ftp服务器需要它的ftp服务连接到一定数量的客户端,所以他们总是支持被动模式的。这就是我们为什么要使用被动模式的原意,为了确保数据可以正确的传输,使用被动模式要明显优于主动模式。(译者注:主动(PORT)模式建立数据传输通道是由服务器端发起的,服务器使用20端口连接客户端的某一个大于1024的 端口;在被动(PASV)模式中,数据传输的通道的建立是由FTP客户端发起的,他使用一个大于1024的端口连接服务器的1024以上的某一个端口)
主动模式传送数据时是“服务器”连接到“客户端”的端口;
被动模式传送数据是“客户端”连接到“服务器”的端口。
主动模式需要客户端必须开放端口给服务器,很多客户端都是在防火墙内,开放端口给FTP服务器访问比较困难。
被动模式只需要服务器端开放端口给客户端连接就行了。
FTP服务器一般都支持主动和被动模式,连接采用何种模式是有FTP客户端软件决定。
FTP被动模式的出现原因
在FTP的历史中,本来只有主动模式的,但是为什么又出现了被动模式呢?这又牵涉到另外一个问题了。
在很久以前(我也不知道多久),地球上还没有什么共享上网这种技术,但是后来出现了,所以也就有了下面的问题,大家都知道,共享上网就是很 多台电脑共享一个公网IP去使用internet,再打个比喻吧,某个局域网共享210.33.25.1这个公网IP上网,当一个内网用户 192.168.0.100去访问外网的FTP服务器时,如果采用主动模式的话,192.168.0.1告诉了FTP服务器我需要某个文件和我打开了x端 口之后,由于共享上网的原因,192.168.0.1在出网关的时候自己的IP地址已经被翻译成了210.33.25.1这个公网IP,所以服务器端收到 的消息也就是210.33.25.1需要某个文件并打开了x端口,FTP服务器就会往210.33.25.1的x端口传数据,这样当然会连接不成功了,因为打开x端口的并不是210.33.25.1这个地址,在这种情况下被动模式就有用了,相信大家已经能够理解被动模式是怎么个连接法了吧。
在主动模式中,FTP的两个端口是相对固定的,如果命令端口是x的话,那数据端口就是x-1,也就是说默认情况下,命令端口是21,数据端口就是20;你把命令端口改成了123,那么数据端口就是122。这样使用防火墙就很方便了,只要开通这两个端口就可以了,但是如果客户端是共享上网的话那岂不是不能正常使用FTP了,这样还是不行,一定需要被动模式。
在被动模式中就麻烦了些,默认情况下命令端口是21,但是数据端口是随机的
因此推荐使用被动模式。
- FTP服务器关闭防火墙
- FTP服务器开启FTP
- Vxworks源码(包括组件ftpLib.c)
- Sylixos的工程文件
base工程选中libVxWorks,app工程进行连接so动态库。
FTP实现依赖TCP进行通信,均使用POSIX标准接口,几乎不需要修改,主要针对警告以及缺失函数部分。
#ifndef SYLIXOS #include#endif #include #ifndef SYLIXOS #include #include #else #include "ftpLib.h" #include "taskLibCommon.h" #include #define log_err(__LOG_LEVEL, ...) do{ if((__LOG_LEVEL) & 1) { if((__LOG_LEVEL) & LOG_ERRNO) { printf("error #"); } printf(__VA_ARGS__);printf("rn"); } }while(0); #define log_info log_err #define log_warning log_err #endif STATUS logLevelChange ( int category, UINT mask ) { return OK; } #ifndef SYLIXOS log_err (FTP_LOG, "Timeout %lu sec.", replyTimeOut.tv_sec); #else log_err (FTP_LOG, "Timeout %lu sec.", (ULONG)replyTimeOut.tv_sec); #endif #ifndef SYLIXOS int len; #else UINT len; #endif #ifndef SYLIXOS bzero ((char *) &dataAddr, sizeof (SOCKADDR)); #else bzero ((char *) &dataAddr, sizeof (SOCKADDR_IN)); #endif #ifndef SYLIXOS int len; #else UINT len; #endif #ifndef SYLIXOS int fromlen = sizeof (from); #else UINT fromlen = sizeof (from); #endif #ifndef SYLIXOS log_err (FTP_LOG, "select timeout after %lu sec.", replyTime.tv_sec); #else log_err (FTP_LOG, "select timeout after %lu sec.", (ULONG)replyTime.tv_sec); #endif
c文件需要将警告部分优化,以及确实函数的实现,h文件主要缺失一些宏定义,进行补全移植全部完成。
#ifndef SYLIXOS #include5. 如何使用FTP#include #endif #ifdef SYLIXOS #include #define MAX_IDENTITY_LEN 100 #define MIN_RESV_PORT 600 #define MAX_RESV_PORT 1023 #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif #define FTP_LOG 1 #define LOG_ERRNO 0x10 #undef FTP_TRANSIENT_MAX_RETRY_COUNT #define FTP_TRANSIENT_MAX_RETRY_COUNT 100 #undef FTP_TRANSIENT_RETRY_INTERVAL #define FTP_TRANSIENT_RETRY_INTERVAL 0 #undef FTP_DEBUG_OPTIONS #define FTP_DEBUG_OPTIONS 0 #undef FTP_TIMEOUT #define FTP_TIMEOUT 0 extern UINT32 ftplTransientMaxRetryCount; extern UINT32 ftplTransientRetryInterval; #endif
一般应用都需要进行初始化才能使用,初始化是可以省略的,只是针对一些时间的设置等。
LOCAL BOOL ftpTransientFatal
(
UINT32 reply
)
{
switch (reply)
{
case (421):
case (450):
case (451):
case (452):
{
return (TRUE);
}
default:
return (FALSE);
}
}
#define FTP_TRANSIENT_FATAL ftpTransientFatal
STATUS usrFtpInit (void)
{
ftpLibInit (FTP_TIMEOUT);
ftplTransientMaxRetryCount = FTP_TRANSIENT_MAX_RETRY_COUNT;
ftplTransientRetryInterval = FTP_TRANSIENT_RETRY_INTERVAL;
ftpTransientFatalInstall ( FTP_TRANSIENT_FATAL );
ftpLibDebugOptionsSet ( FTP_DEBUG_OPTIONS );
return OK;
}
FTP使用主要使用ftpXfer函数进行文件的上传和下载。ftpXfer函数内部的函数也可单独使用,如果有需求可参照ftpXfer函数自己定制。
STATUS ftpXfer
(
char *host,
char *user,
char *passwd,
char *acct,
char *cmd,
char *dirname,
char *filename,
int *pCtrlSock,
int *pDataSock
)
文件上传和下载例子。
#include#include "ftpLib.h" #include "socket.h" #define HOST "192.168.1.10" #define USER "anonymous" #define PWD " " #define W_DIR "/ftptest" #define RD_CMD "RETR %s" #define WR_CMD "STOR %s" #define FILE "fromserver.txt" #define TOSERVER_FILE "toserver.txt" void FtpDownload() { int ctrlSock; int dataSock; char buf[512]; int nBytes; if(ERROR == ftpXfer(HOST, USER, PWD, "", RD_CMD, W_DIR, FILE, &ctrlSock, &dataSock)) { printf("rn ftp connected failed!"); return ; } while((nBytes = read(dataSock, buf, sizeof(buf))) > 0) { printf("rn it is [%s]", buf); } close(dataSock); if(ERROR == nBytes) printf("rnit is reading error."); if(ftpCommand(ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0) == FTP_COMPLETE) printf("rnftp is completed"); close(ctrlSock); } void FtpUpload () { int ctrlSock; int dataSock; int nBytes; #define FTP_WRITE_TIMEOUT_DEFAULT 30 struct timeval tm; tm.tv_sec = FTP_WRITE_TIMEOUT_DEFAULT; tm.tv_usec = 0; if(ERROR == ftpXfer(HOST, USER, PWD, "", WR_CMD, W_DIR, TOSERVER_FILE, &ctrlSock, &dataSock)) { printf("rn ftp connected failed!"); return ; } setsockopt(dataSock,SOL_SOCKET,SO_SNDTIMEO,(char*)&tm,sizeof(struct timeval)); const PCHAR const ptr = "This is a test!."; nBytes = write (dataSock, ptr, strlen(ptr)); close(dataSock); if(ERROR == nBytes) printf("rnit is reading error."); if(ftpCommand(ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0) == FTP_COMPLETE) printf("rnftp is completed"); close(ctrlSock); }
可以看到使用了两个信道,命令信道和数据信道。
命令
控制信道进行命令的传输,简要列举了一些控制命令。
大概了解FTP原理即可,ftpLib已经实现了FTP功能,知道两个信道,使用时就得心应手了。



