类别 | 内容 |
---|---|
关键词 | Sylixos FTP Vxworks |
摘 要 | Vxworks组件FTP移植和使用 |
目录
1. 适用范围
本文适用对FTP有使用需求的工程师,讲述从FTP移植到使用。
2. 原理概述
文件传输协议(File Transfer Protocol,FTP)是用于在网络上进行文件传输的一套标准协议,它工作在 OSI 模型的第七层, TCP 模型的第四层, 即应用层, 使用 TCP 传输而不是 UDP, 客户在和服务器建立连接前要经过一个“三次握手”的过程, 保证客户与服务器之间的连接是可靠的, 而且是面向连接, 为数据传输提供可靠保证。
FTP允许用户以文件操作的方式(如文件的增、删、改、查、传送等)与另一主机相互通信。然而, 用户并不真正登录到自己想要存取的计算机上面而成为完全用户, 可用FTP程序访问远程资源, 实现用户往返传输文件、目录管理以及访问电子邮件等等, 即使双方计算机可能配有不同的操作系统和文件存储方式。
2.1 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,但是数据端口是随机的
因此推荐使用被动模式。
3. 准备工作
3.1 环境准备
- FTP服务器关闭防火墙
- FTP服务器开启FTP
3.2 资源准备
- Vxworks源码(包括组件ftpLib.c)
- Sylixos的工程文件
4. 技术实现
4.1 base工程和app工程设置
base工程选中libVxWorks,app工程进行连接so动态库。
4.2 ftpLib移植
FTP实现依赖TCP进行通信,均使用POSIX标准接口,几乎不需要修改,主要针对警告以及缺失函数部分。
#ifndef SYLIXOS
#include <remLib.h>
#endif
#include <hostLib.h>
#ifndef SYLIXOS
#include <ftpLib.h>
#include <applUtilLib.h>
#else
#include "ftpLib.h"
#include "taskLibCommon.h"
#include <SylixOS.h>
#define log_err(__LOG_LEVEL, ...) \
do{\
if((__LOG_LEVEL) & 1)\
{\
if((__LOG_LEVEL) & LOG_ERRNO)\
{\
printf("error #");\
}\
printf(__VA_ARGS__);printf("\r\n");\
}\
}while(0);
#define log_info log_err
#define log_warning log_err
#endif
STATUS logLevelChange
(
int category, /* logger category */
UINT mask /* bit mask of levels to log at - 0x00..0xff */
)
{
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
#include <vxWorks.h>
#include <vwModNum.h>
#endif
#ifdef SYLIXOS
#include <vxWorksCommon.h>
#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 /* log errno */
#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; /* Retry just once for now */
extern UINT32 ftplTransientRetryInterval; /* Default with no delay */
#endif
5. 如何使用FTP
一般应用都需要进行初始化才能使用,初始化是可以省略的,只是针对一些时间的设置等。
/*******************************************************************************
*
* ftpTransientFatal - example applette to terminate FTP transient host responses
*
* ftpXfer will normally retry a command if the host responds with a 4xx
* reply. If this applette is installed, it can immediately terminate
* the retry sequence.
*
*
* RETURNS : TRUE - Terminate retry attempts
* FALSE - Continue retry attempts
*
*
* SEE ALSO : ftpTransientFatalInstall(), ftpTransientConfigSet()
*
*/
LOCAL BOOL ftpTransientFatal
(
UINT32 reply /* Three digit code defined in RFC #640 */
)
{
switch (reply)
{
case (421): /* Service not available */
case (450): /* File unavailable */
case (451): /* error in processing */
case (452): /* insufficient storage */
{
/* yes, these are actually non-recoverable replies */
return (TRUE);
}
/* attempt to retry the last command */
default:
return (FALSE);
}
}
#define FTP_TRANSIENT_FATAL ftpTransientFatal
/******************************************************************************
*
* usrFtpInit - initialize FTP parameters
*
* This routine configures ftp client parameters.
* defined by the user via the project facility.
*
* RETURNS: OK or ERROR
*
* NOMANUAL
*/
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, /* name of server host */
char *user, /* user name for host login */
char *passwd, /* password for host login */
char *acct, /* account for host login */
char *cmd, /* command to send to host */
char *dirname, /* directory to 'cd' to before sending command */
char *filename, /* filename to send with command */
int *pCtrlSock, /* where to return control socket fd */
int *pDataSock /* where to return data socket fd, */
/* (NULL == don't open data connection) */
)
文件上传和下载例子。
#include <stdio.h>
#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"
/*
* ftp 下载一个文件
*/
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("\r\n ftp connected failed!");
return ;
}
while((nBytes = read(dataSock, buf, sizeof(buf))) > 0)
{
printf("\r\n it is [%s]", buf);
}
close(dataSock);
if(ERROR == nBytes)
printf("\r\nit is reading error.");
if(ftpCommand(ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0) == FTP_COMPLETE)
printf("\r\nftp is completed");
close(ctrlSock);
}
/*
* ftp 上传一个文件
*/
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("\r\n 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("\r\nit is reading error.");
if(ftpCommand(ctrlSock, "QUIT", 0, 0, 0, 0, 0, 0) == FTP_COMPLETE)
printf("\r\nftp is completed");
close(ctrlSock);
}
可以看到使用了两个信道,命令信道和数据信道。
命令
控制信道进行命令的传输,简要列举了一些控制命令。
6. 总结
大概了解FTP原理即可,ftpLib已经实现了FTP功能,知道两个信道,使用时就得心应手了。