FTP协议编程看这一篇文章就够了

1、FTP 概述

     文件传输协议(FTP)作为网络共享文件的传输协议,在网络设备中具有广泛的应用,FTP协议用于实现网络中不同设备之间的文件传输与共享,这个是一个网络协议与具体的操作系统无关,只要你所使用的操作系统支持此协议就可以进行文件传输。

      FTP协议中的设备分为客户端和服务器2种角色,在传输文件时,FTP 客户端程序先与服务器建立连接,然后向服务器发送命令。服务器收到命令后给予响应,执行相应命令的操作。本文中所模拟的FTP通信协议的过程,客户端使用windows电脑,在电脑上打开一个tcp调度工具,服务器是阿里云的服务器,操作系统是Linux,提前在服务器上安装vsftp,并且做好相关设置。具体的设置可以参考这个文章。当然也可以在本地网络中找2台电脑,一台电脑做为FTP服务器,运行FileZilla Server软件即可以实现,另外一台电脑做为客户端。

2、FTP 协议

      HTTP协议是基于TCP协议之上的简单的协议,FTP 协议相比它要复杂一点,但是当你看完这篇文件后,会感觉FTP协议其实也还是很简单。HTTP协议通过一个SOCKET连接传输一次会话数据,而FTP协议中将控制命令与数据分开传送的方法提高了效率,FTP协议要创建2个SOCKET TCP连接,占用2个TCP 端口,一个连接用于传输控制命令,另外一个连接用于传输数据。

      FTP 使用 2 个端口,一个数据端口和一个命令端口(也叫做控制端口)。这两个端口一般是21 (命令端口)和 20 (数据端口)。控制 Socket 用来传送命令,数据 Socket 是用于传送数据。命令端口中客户端每一个 FTP 命令发送之后,FTP 服务器都会返回一个字符串,其中包括一个响应代码和一些说明信息。其中的返回码主要是用于判断命令是否被成功执行了。

2.1、命令端口

     客户端创建一个 Socket 连接 FTP 服务器的21端口,此控制连接实现 FTP 命令的发送和接收返回的响应信息。比如:“登录”、“改变目录”、“删除文件”,“上传文件”,“下载文件”这些操作。

2.2、数据端口

      对于有数据传输的操作,客户端创建一个SOCKET连接, 主要传输显示目录列表,上传、下载文件

     在数据传输时,有两种模式:被动模式、主动模式。

     使用被动模式,通常FTP服务器端会返回给客户端一个端口号(这个端口由FTP服务器自动分配出来)。客户端需要另开一个 Socket 来连接FTP服务器这个端口,在这个TCP连接会话中FTP服务器做为TCP服务器,FTP客户端做为TCP客户端,然后根据FTP客户端操作来发送命令,数据会通过这个新开的一个端口传输。

     使用主动模式,通常FTP客户端会发送一个端口号给FTP服务器,并在这个端口监听。这个TCP连接会话中FTP服务器做为TCP客户端,FTP客户端做为TCP服务器。FTP服务器需要连接到FTP客户端开启的这个数据端口,并进行数据的传输。

 

2.3、主动模式 (PORT)

       主动模式下,FTP客户端随机打开一个大于 1024 的端口向服务器的命令端口21,创建控制连接。同时客户端开放N +1 端口监听,并向服务器发出 “port N+1” 命令,由服务器从它自己的数据端口 (20) 主动连接到客户端指定的数据端口 (N+1)。

       FTP 的客户端只是告诉服务器自己的端口号,等待服务器来连接客户端这个的端口。一般这种情况下服务器要从公网向客户端所在的内网发过连接,有些客户端的内网为了保证网络安全,内网防水墙是阻止外网访问内网的设备

2.4、被动模式 (PASV)

       为了解决服务器发起内网客户端会被拦截的问题,FTP协议提供另外一种连接方式:即被动方式。命令连接和数据连接都由客户端发起。

      被动模式下,FTP客户端连接服务器的命令端口21,创建控制连接,然后服务器会分配一个端口(端口号大于1024),并且返回给客户端已经分配的端口号和连接的IP地址。等待客户端来连接这个端口,创建数据连接,文件传输和显示目录列表这些信号都在这个数据连接中传输。

 

 

2.5、 FTP 命令

       FTP 每个命令都有 3 到 4 个字母组成,命令后面跟参数,用空格分开。每个命令最后都以 "\r\n"结尾,FTP命令在控制连接中传输。上传或下载一个文件时,FTP客户端要首先登陆FTP服务器(输入用户名和密码),切换到要读定的服务器目录,上传或下载文件,最后退出登陆。这个过程中用的命令有USER, PASS, PASV, PORT,CWD,RETR,QUIT等命令。

 

要下载或上传一个文件,首先要登入 FTP 服务器,然后发送命令,最后退出。这个过程中,主要用到的命令有 USER、PASS、SIZE、REST、CWD、RETR、PASV、PORT、QUIT。

USER: 指定用户名。控制连接发现的第一条的命令,向服务器输入登陆的用户句。例如 “USER zsm\r\n”: 登陆用户名为zsm。输入的用户名一定是在FTP服务器提前注册好的用户名,否则FTP服务器对输入的用户名返回错误。

PASS: 指定用户密码。该命令紧跟 USER 命令后。“PASS 1982hello\r\n”:密码为 1982hello。

SIZE: 从服务器上返回指定文件的大小。“SIZE file.txt\r\n”:如果 file.txt 文件存在,则返回该文件的大小。

CWD: 改变工作目录。如:“CWD /HDRC-VC0/\r\n”,让服务器从切换到HDRC-VC0这个文件夹下面。

PASV: 让服务器在数据端口监听,进入被动模式。如:“PASV\r\n”。此时服务器会给客户端返回一个IP地址端口号,例如服务器会返回227 Entering Passive Mode (172,17,92,131,31,139),227是返回的应答码,后面字符串表示服务器进入了被动模式,括号中的6个数字,前4个数据是服务器的IP地址,后2个数据表示端口号,端口号的计算方法是第一个数字乘以256加上第二个数据,即31*256+139=8075,端口号为8075,客户端需要连接8075端口来建立数据连接。

PORT: 告诉 FTP 服务器客户端监听的端口号,让 FTP 服务器进入主动模式,服务器主动连接客户端端口来建立数据连接。如:“PORT h1,h2,h3,h4,p1,p2”, 服务器获得到客户端开放的端口是p1*256+p2,服务器会连接客户端的这个端口来创建数据连接。

RETR: 下载文件。“RETR file.txt \r\n”:下载文件 file.txt。

STOR: 上传文件。“STOR file.txt\r\n”:上传文件 file.txt。

REST: 该命令是文件内数据偏移指令,相当于文件操作的fseek函数,移动文件读写指针。此命令后应该跟其它要求文件传输的 FTP 命令。“REST 100\r\n”:重新指定文件传送的偏移量为 100 字节。

QUIT: 关闭与服务器的连接,退出登陆。

 

2.6、FTP 响应码

    客户端发送 FTP 命令后,服务器返回响应码。

   响应码用三位数字编码表示:

    第一个数字给出了命令状态的一般性指示,比如响应成功、失败或不完整。含义如下

             1 表示服务器正确接收信息,还未处理。

             2 表示服务器已经正确处理信息。

             3 表示服务器正确接收信息,正在处理。

             4 表示信息暂时错误。

            5 表示信息永久错误。

    第二个数字是响应类型的分类,如 2 代表跟连接有关的响应,3 代表用户认证。

 

 

 

 

 

 

 

二个数字的含义如下:

 

0 表示语法。

 

1 表示系统状态和信息。

2 表示连接状态。

3 表示与用户认证有关的信息。

4 表示未定义。

5 表示与文件系统有关的信息。

2.7、socket 编程

Socket 客户端编程主要步骤如下:

  1. socket() 创建一个 Socket
  2. connect() 与服务器连接
  3. write() 和 read() 进行会话
  4. close() 关闭 Socket连接

Socket 服务器端编程主要步骤如下:

  1. socket() 创建一个 Socket
  2. bind()
  3. listen() 监听
  4. accept() 接收连接的请求
  5. write() 和 read() 进行会话
  6. close() 关闭 Socket连接

2.8、使用TCP工具来模拟FTP通信数据

       下面我们使用TCP工具向服务器创建连接来模拟与服务器的通信数据,深入学习FTP数据在网络上是如何编码和传输的。

第一步:打开TCP&UDP测试软件,这个软件你可以自行去网上下载,创建一个TCP控制连接,输入IP地址和端口号(21),创建连接

    

      服务器会返回一条欢迎语,"220 (vsFTPd 3.0.2)\r\n",其中220是应答码,表示服务器正确的接受了连接,软件窗口中无法显示出\r\n所以看不到,vsFTPd 3.0.2是欢迎语,不同的FTP服务器返回的不一样。如下图

         第二步,发送登陆用户名称密码,先发送用户名,在上面软件的发送窗口中输入"USER ftpadmin\r\n"由于这个软件窗口中无法输入\r\n,所以采用这个字符串对应的16进制数据发送,即在上面的输入中输入55 53 45 52 20 66 74 70 61 64 6D 69 6E 0D 0A来表示这个字符串,以下的所有操作最后都是输入的字符串对应的16进制数据。服务器返回:“331 Please specify the password.\r\n”

         再发送密码,“PASS 1982long\r\n”,对应的16进制数据:50 41 53 53 20 31 39 38 32 6C 6F 6E 67 0D 0A,服务器返回:“230 Login successful\r\n”

        第三步、切换工作目录到HDRC-VC0这个目录,客户端发送“CWD /HDRC-VC0\r\n"  对应的16进制数据43 57 44 20 2F 48 44 52 43 2D 56 43 30 0D 0A
服务器返回:“250 Directory successfully changed\r\n”

       第四步、设置文件传输方式,文件传输方式有bin,ascii字符的方式,对于传输bin,图片等数据请使用bin文件的方式,ascii字符的传输方式会因为不同的操作系统对传输的内容进行转码。客户端发送”TYPE I\r\n“ 对应的16进制数据54 59 50 45 20 49 0D 0A   服务器返回:”200 Switching to Binary mode.\r\n“

        第五步、让服务器进行被动模式,客户端发送"PASV\r\n"  对应的16进制数据  50 41 53 56 0D 0A,服务器返回
”227 Entering Passive Mode (172,17,92,131,31,139)\r\n“.   即服务器开放端口31*256+139=8075端口传输文件
此时要让客户端新建一个连接到服务器的8075端口,用于接收数据。

         第六步、下载文件,客户端发送:”RETR HDRC-VC0_V12_20190717.bin\r\n“  对应的16进制数据52 45 54 52 20 48 44 52 43 2d 56 43 30 5f 56 31 32 5f 32 30 31 39 30 37 31 37 2e 62 69 6e 0d 0a服务器返回:”
150 Opening BINARY mode data connection for HDRC-VC0_V12_20190717.bin (90824 bytes).\r\n“,同时服务器开始使用数据连接向客户端发送HDRC-VC0_V12_20190717.bin文件的内容数据。发送完成后,服务器在控制连接中返回一条应答”226 Transfrer complete.\r\n“表示传输完成。

       第七步、退出登陆,关闭连接。客户端发送”QUIT\r\n“,服务器返回:”221 Logout\r\n“

        上传文件与下载文件一样,不再举例介绍。

2.9  编程实现一个文件下载的功能

       本程序运行在RT thread操作系统之上,使用标准TCP socket接口编程,你也可以把此种移植到其他系统上面来使用。

        下面是文件ftp.c的源代码。

#include <sys/socket.h>
#include <sys/select.h>
#include <netdb.h>

#include "main.h"
#include "ftp.h"
#include "toolkit\toolkit.h"

#ifdef INCLUDE_FTP

#define DBG_ENABLE
#define DBG_SECTION_NAME "FTP"
#define DBG_LEVEL        DBG_INFO
#include <rtdbg.h>

/*FTP通信管理数据结构*/
FTP_MANAGER   g_ftp_manager;


/*********************************************************************************************************
** Function name:      ftp_wait_server_ack()
** Descriptions:        FTP客户端等待服务器应答
** input parameters:   无
** output parameters:  无
** Returned value:      无
*********************************************************************************************************/
static rt_err_t ftp_wait_server_ack(int socket,  rt_uint32_t *ack_code, rt_uint32_t wait_time)
{
    fd_set readset;
    struct timeval timeout;
    char     ftp_recv_buf[50] = {0};
    int            recv_len = 0;
    int            code= 0;

    timeout.tv_sec = wait_time;
    timeout.tv_usec = 0;

    FD_ZERO(&readset);
    FD_SET(socket, &readset);

    if (select(socket + 1, &readset, RT_NULL, RT_NULL, &timeout) <= 0) {
        LOG_E("select the socket timeout!");
        return -RT_ETIMEOUT;
    }

    // Wait and receive the packet back from the server. If n == -1, it failed.
    recv_len = recv(socket, (char*)ftp_recv_buf, sizeof(ftp_recv_buf), 0);

    if (recv_len < 0) {
        LOG_E("reading from socket error!");
        return -RT_ERROR;
    }

    if(1 == sscanf(ftp_recv_buf, "%d", &code))
    {
        *ack_code = code;
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }


}

/*********************************************************************************************************
** Function name:      ftp_send_cmd_and_server_ack()
** Descriptions:        FTP客户端发送命令等待服务器应答
** input parameters:   无
** output parameters:  无
** Returned value:      无
*********************************************************************************************************/
static rt_err_t ftp_send_cmd_and_server_ack(int socket, char *cmd, rt_uint32_t cmd_len,rt_uint32_t *ack_code, rt_uint32_t wait_time)
{
    fd_set readset;
    struct timeval timeout;
    char          ftp_recv_buf[100] = {0};
    int            len = 0;
    int            code= 0;

    if((RT_NULL == cmd) || (RT_NULL == ack_code))
    {
        return -RT_ERROR;
    }
    
    timeout.tv_sec = wait_time;
    timeout.tv_usec = 0;

    FD_ZERO(&readset);
    FD_SET(socket, &readset);

    /*发送指令*/
    len = send(socket, (char*)cmd, cmd_len, 0);

    if (len < 0) {
        LOG_E("send cmd error!");
        return -RT_ERROR;
    }
    
    /*等待应答*/
    if (select(socket + 1, &readset, RT_NULL, RT_NULL, &timeout) <= 0) {
        LOG_E("select the socket timeout!");
        return -RT_ETIMEOUT;
    }

    // Wait and receive the packet back from the server. If n == -1, it failed.
    len = recv(socket, (char*)ftp_recv_buf, sizeof(ftp_recv_buf), 0);

    if (len < 0) {
        LOG_E("reading from socket error!");
        return -RT_ERROR;
    }

    if(1 == sscanf(ftp_recv_buf, "%d", &code))
    {
        *ack_code = code;
        return RT_EOK;
    }
    else
    {
        LOG_I("Rcv data:%s", ftp_recv_buf);
        return -RT_ERROR;
    }


}



/*********************************************************************************************************
** Function name:      ftp_pasv_mode()
** Descriptions:        FTP客户端发送命令给服务器进入pasv模式
** input parameters:   无
** output parameters:  无
** Returned value:      无
*********************************************************************************************************/
static rt_err_t ftp_filesize_get(int socket, char *cmd, rt_uint32_t cmd_len, rt_uint32_t *ack_code,  rt_uint32_t *file_size, rt_uint32_t wait_time)
{
    fd_set readset;
    struct timeval timeout;
    char           ftp_recv_buf[100] = {0};
    int            len = 0;
    int            code= 0;
    int            a= 0;

    if((RT_NULL == ack_code) || (RT_NULL == file_size) ||
       (RT_NULL ==  cmd))
    {
        return -RT_ERROR;
    }

    timeout.tv_sec = wait_time;
    timeout.tv_usec = 0;

    FD_ZERO(&readset);
    FD_SET(socket, &readset);

    /*发送指令*/
    len = send(socket, cmd, cmd_len, 0);

    if (len < 0) {
        LOG_E("send cmd error!");
        return -RT_ERROR;
    }
    
    /*等待应答*/
    if (select(socket + 1, &readset, RT_NULL, RT_NULL, &timeout) <= 0) {
        LOG_E("select the socket timeout!");
        return -RT_ETIMEOUT;
    }

    // Wait and receive the packet back from the server. If n == -1, it failed.
    len = recv(socket, (char*)ftp_recv_buf, sizeof(ftp_recv_buf), 0);

    if (len < 0) {
        LOG_E("reading from socket error!");
        return -RT_ERROR;
    }
    
    /*获取文件大小和应答码*/
    if(2 == sscanf(ftp_recv_buf, "%d %d", &code, &a))
    {
        *ack_code = code;
        *file_size = a;
        return RT_EOK;
    }
    else
    {
        LOG_E("Rcv data:%s", ftp_recv_buf);
        return -RT_ERROR;
    }


}



/*********************************************************************************************************
** Function name:      ftp_pasv_mode()
** Descriptions:        FTP客户端发送命令给服务器进入pasv模式
** input parameters:   无
** output parameters:  无
** Returned value:      无
*********************************************************************************************************/
static rt_err_t ftp_pasv_mode(int socket,rt_uint32_t *ack_code,  rt_uint32_t *server_port, rt_uint32_t wait_time)
{
    fd_set readset;
    struct timeval timeout;
    char           ftp_recv_buf[80] = {0};
    int            len = 0;
    int            code= 0;
    int            a,b,c,d,e,f = 0;

    if((RT_NULL == ack_code) || (RT_NULL == server_port))
    {
        return -RT_ERROR;
    }

    timeout.tv_sec = wait_time;
    timeout.tv_usec = 0;

    FD_ZERO(&readset);
    FD_SET(socket, &readset);

    /*发送指令*/
    len = send(socket, "PASV\r\n", strlen("PASV\r\n"), 0);

    if (len < 0) {
        LOG_E("send cmd error!");
        return -RT_ERROR;
    }
    
    /*等待应答*/
    if (select(socket + 1, &readset, RT_NULL, RT_NULL, &timeout) <= 0) {
        LOG_E("select the socket timeout!");
        return -RT_ETIMEOUT;
    }

    // Wait and receive the packet back from the server. If n == -1, it failed.
    len = recv(socket, (char*)ftp_recv_buf, sizeof(ftp_recv_buf), 0);

    if (len < 0) {
        LOG_E("reading from socket error!");
        return -RT_ERROR;
    }
    
    /*跳过非数字字符来取出IP和端口号*/
    if(7 == sscanf(ftp_recv_buf, "%d%*[^(](%d,%d,%d,%d,%d,%d", &code,&a,&b,&c,&d,&e,&f))
    {
        *ack_code = code;
        *server_port= e *256+f;
        return RT_EOK;
    }
    else
    {
        LOG_I("Rcv data:%s", ftp_recv_buf);
        return -RT_ERROR;
    }


}

/*********************************************************************************************************
** Function name:      ftp_data_thread()
** Descriptions:        ftp数据通信线程
** input parameters:   无
** output parameters:  无
** Returned value:      无
*********************************************************************************************************/
static void ftp_data_thread(void *parameter)
{
    struct timeval timeout;
    fd_set readset;
    char     ftp_data_buf[1400] = {0};
    int      recv_len = 0;
    int      socket = 0;
    rt_uint32_t recv_sum = 0;

    socket = g_ftp_manager.ftp_data_socket;
    
    FD_ZERO(&readset);

    timeout.tv_sec = 20;
    timeout.tv_usec = 0;

    
    for(;;)
    {
        FD_SET(socket, &readset);
        if (select(socket + 1, &readset, RT_NULL, RT_NULL, &timeout) <= 0) {
            LOG_E("select data socket timeout!");
            break;
        }

        // Wait and receive the packet back from the server. If n == -1, it failed.
        recv_len = recv(socket, (char*)ftp_data_buf, sizeof(ftp_data_buf), 0);

        if (recv_len <= 0) 
        {
            LOG_E("reading data socket error!");
            break;
        }
        else
        {   
            /*这里就是接收到的文件内容数据,只是打印出接收的文件长度,并没有存储到本地*/
            recv_sum = recv_sum + recv_len;
            LOG_I("Rcv sum = %d, len = %d", recv_sum, recv_len);

            if(recv_sum == g_ftp_manager.file_size)
            {
                LOG_I("Rcv complete!", recv_sum, recv_len);
                break;
            }
        }

    
    }

    closesocket(g_ftp_manager.ftp_data_socket);
    g_ftp_manager.ftp_data_socket = -1;


}

/*********************************************************************************************************
** Function name:      ftp_control_thread()
** Descriptions:        FTP控制线程
** input parameters:   无
** output parameters:  无
** Returned value:      无
*********************************************************************************************************/
static void ftp_control_thread(void *parameter)
{
    rt_thread_t  ftp_thread = NULL;
    struct hostent *server = RT_NULL;
    struct sockaddr_in serv_addr;
    rt_uint32_t  ack_code = 0;
    char        cmd_buf[50] = {0};
    rt_uint32_t  server_port = 0;

    g_ftp_manager.ftp_control_socket = socket(AF_AT, SOCK_STREAM, 0); // Create a UDP socket.

    if (g_ftp_manager.ftp_control_socket < 0) {
        LOG_E("opening control socket");
        return;
    }

    /*服务器地址转换成IP地址*/
    server = gethostbyname(FTP_SVR_ADDR); // Convert URL to IP.

    if (server == NULL) {
        LOG_E("Get ftp server hostname!");
        goto ftp_error;
    }

    // Zero out the server address structure.

    memset((char *) &serv_addr, 0, sizeof(serv_addr));

    
    /* 初始化预连接的服务端地址 */
    serv_addr.sin_family = AF_AT;
    serv_addr.sin_port = htons(FTP_SVR_PORT);
    serv_addr.sin_addr = *((struct in_addr *)server->h_addr);
    rt_memset(&(serv_addr.sin_zero), 0, sizeof(serv_addr.sin_zero));

    // Call up the server using its IP address and port number.

    if (connect(g_ftp_manager.ftp_control_socket, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
    {
        
        LOG_E("connecting faile");
        goto ftp_error;
    }

    /*等待服务器发来的欢迎登陆请求*/
    if((RT_EOK != ftp_wait_server_ack(g_ftp_manager.ftp_control_socket, &ack_code, 5)) || (ack_code != 220))
    {
        LOG_E("Server not ack welcome");
        goto ftp_error;
    }

    /*登陆服务器输入用户名*/
    sprintf(cmd_buf, "USER %s\r\n", FTP_USER_NAME);
    if((RT_EOK != ftp_send_cmd_and_server_ack(g_ftp_manager.ftp_control_socket, cmd_buf, strlen(cmd_buf), &ack_code, 5)) ||
       (ack_code != 331))
    {   
        LOG_E("Login user error!");
        goto ftp_error;
    }

    /*登陆服务器输入密码*/
    sprintf(cmd_buf, "PASS %s\r\n", FTP_PASSWORD);
    if((RT_EOK != ftp_send_cmd_and_server_ack(g_ftp_manager.ftp_control_socket, cmd_buf, strlen(cmd_buf), &ack_code, 5)) ||
       (ack_code != 230))
    {   
        LOG_E("Login pass error!");
        goto ftp_error;
    }

    /*切换当前工作目录*/
    sprintf(cmd_buf, "CWD %s\r\n", FTP_SVR_PATH);
    if((RT_EOK != ftp_send_cmd_and_server_ack(g_ftp_manager.ftp_control_socket, cmd_buf, strlen(cmd_buf), &ack_code, 5)) ||
       (ack_code != 250))
    {   
        LOG_E("Switch dir error!");
        goto ftp_error;
    }

    /*切换二进制传输模式*/
    strcpy(cmd_buf, "TYPE I\r\n");
    if((RT_EOK != ftp_send_cmd_and_server_ack(g_ftp_manager.ftp_control_socket, cmd_buf, strlen(cmd_buf), &ack_code, 5)) ||
       (ack_code != 200))
    {   
        LOG_E("Switch dir error!");
        goto ftp_error;
    }

    /*获取下载文件大小*/
    sprintf(cmd_buf, "SIZE %s\r\n", g_ftp_manager.file_name);
    if((RT_EOK != ftp_filesize_get(g_ftp_manager.ftp_control_socket, cmd_buf, strlen(cmd_buf), &ack_code, &g_ftp_manager.file_size, 5)) ||
       (ack_code != 213))
    {   
        LOG_E("Get filesize error!");
        goto ftp_error;
    }
    LOG_I("File size %d", g_ftp_manager.file_size);

    /*获取服务器开放的端口号*/
    if((RT_EOK != ftp_pasv_mode(g_ftp_manager.ftp_control_socket,  &ack_code, &server_port, 5)) ||
       (ack_code != 227))
    {   
        LOG_E("PASV error!");
        goto ftp_error;
    } 

    

    /*创建数据连接*/
    g_ftp_manager.ftp_data_socket = socket(AF_AT, SOCK_STREAM, 0); 

    if (g_ftp_manager.ftp_data_socket < 0) {
        LOG_E("opening data socket");
        goto ftp_error;
    }


    /* 初始化预连接的服务端地址 */
    serv_addr.sin_family = AF_AT;
    serv_addr.sin_port = htons(server_port);
    serv_addr.sin_addr = *((struct in_addr *)server->h_addr);
    rt_memset(&(serv_addr.sin_zero), 0, sizeof(serv_addr.sin_zero));

    // Call up the server using its IP address and port number.

    if (connect(g_ftp_manager.ftp_data_socket, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
    {
        
        LOG_E("data connecting faile");
        goto ftp_error;
    }

    /*创建数据连接接收线程*/
    ftp_thread = rt_thread_create("ftp_data",
                                     (void (*)(void *parameter))ftp_data_thread,
                                     RT_NULL,
                                     2048,
                                     24 ,
                                     15);
    if (ftp_thread == RT_NULL)
    {
        LOG_E("Creat ftp data thread fail!");
        goto ftp_error;
    }  
    else
    {
        rt_thread_startup(ftp_thread);
    }

    /*等待数据接收线程启动*/
    rt_thread_mdelay(1000);

    /*下载文件*/
    sprintf(cmd_buf, "RETR %s\r\n", g_ftp_manager.file_name);
    if((RT_EOK != ftp_send_cmd_and_server_ack(g_ftp_manager.ftp_control_socket, cmd_buf, strlen(cmd_buf), &ack_code, 5)) ||
       (ack_code != 150))
    {   
        LOG_E("Download file error!");
        goto ftp_error;
    }

    /*等待下载完成*/
    if((RT_EOK != ftp_wait_server_ack(g_ftp_manager.ftp_control_socket, &ack_code, 50)) || (ack_code != 226))
    {
        LOG_E("Server not ack download complete");
        
    }
    else
    {
        LOG_I("Server ack download complete");
    }
  
    
    /*退出登陆*/
    strcpy(cmd_buf, "QUIT\r\n");
    if((RT_EOK != ftp_send_cmd_and_server_ack(g_ftp_manager.ftp_control_socket, cmd_buf, strlen(cmd_buf), &ack_code, 5)) ||
       (ack_code != 221))
    {   
        LOG_E("Server not logout!");
        goto ftp_error;
    }
    


ftp_error:
    closesocket(g_ftp_manager.ftp_control_socket);
    g_ftp_manager.ftp_control_socket= -1;

    if(g_ftp_manager.ftp_data_socket != -1)
    {
        closesocket(g_ftp_manager.ftp_data_socket); 
        g_ftp_manager.ftp_data_socket = -1;
    }
    

}

    下面是ftp.h的头文件定义。

#ifndef __FTP_H_
#define  __FTP_H_


/*********************************************************************************************************
*        定义FTP的默认用户名称,服务器地址,端口等
*********************************************************************************************************/
#define FTP_SVR_ADDR    "www.rXXXw-lXXXing.com"
#define FTP_SVR_PORT    21
#define FTP_SVR_PATH    "/XXXX/"
#define FTP_USER_NAME   "XXX"
#define FTP_PASSWORD    "1XXXg"
#define FTP_FILENAME    "HDRC-VC0_V10.bin"

/*********************************************************************************************************
*        定义FTP通信管理数据结构
*********************************************************************************************************/
typedef struct FTP_MANAGER_STRUCT
{
    int            ftp_control_socket;                                             /* FTP控制socket    */
    int            ftp_data_socket;                                                /* FTP数据socket    */
    char           file_name[30];                                                  /* 下载文件名称     */
    rt_uint32_t    file_size;                                                       /* 文件大小         */
   
}FTP_MANAGER;

 

 

 

发布了37 篇原创文章 · 获赞 79 · 访问量 15万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章