zoukankan      html  css  js  c++  java
  • Socket网络编程--FTP客户端(1)(Windows)

      已经好久没有写过博客进行分享了。具体原因,在以后说。

      这几天在了解FTP协议,准备任务是写一个FTP客户端程序。直接上干货了。

    0.了解FTP作用

      就是一个提供一个文件的共享协议。

    1.了解FTP协议

      FTP有指令和响应码。FTP 控制帧即指 TELNET 交换信息,包含 TELNET 命令和选项。然而,大多数 FTP 控制帧是简单的 ASCII 文本,可以分为 FTP 命令或 FTP 消息。 FTP 消息是对 FTP 命令的响应,它由带有解释文本的应答代码构成。

      像这种利用交换信息来进行简单的控制,这种协议,还真的很好玩的说。 命令与响应码部分信息如下

      

      

    2. 安装一个FTP服务器

      我们先安装一个FTP服务器,用于测试,这里是用FileZilla Server作为FTP服务器。

      启动后,增加一个用户user/user

    3.FTP客户端源代码讲解

      下面这个是FTPAPI.h文件

     1 #ifndef FTPAPI_H_INCLUDED
     2 #define FTPAPI_H_INCLUDED
     3 
     4 #include <stdio.h>
     5 #include <winsock2.h>
     6 
     7 SOCKET socket_connect(char *host, int port);
     8 SOCKET connect_server(char *host, int port);
     9 int ftp_sendcmd_re(SOCKET sock, char *cmd, char *re_buf, ssize_t *len);
    10 int ftp_sendcmd(SOCKET sock, char *cmd);
    11 int login_server(SOCKET sock, char *user, char *pwd);
    12 void socket_close(int c_sock);
    13 
    14 
    15 /**********可用命令*********/
    16 SOCKET ftp_connect(char *host, int port, char *user, char *pwd); //连接到服务器
    17 int ftp_quit(SOCKET sock); //断开连接
    18 int ftp_type(SOCKET sock, char mode); //设置FTP传输类型
    19 int ftp_cwd(SOCKET sock, char *path); //更改工作目录
    20 int ftp_cdup(SOCKET sock); //回到上级目录
    21 int ftp_mkd(SOCKET sock, char *path); //创建目录
    22 SOCKET ftp_pasv_connect(SOCKET c_sock); //连接到PASV接口
    23 int ftp_list(SOCKET c_sock, char *path, char **data, int *data_len); //列出FTP工作空间的所有目录
    24 int ftp_deletefolder(SOCKET sock, char *path); //删除目录
    25 int ftp_deletefile(SOCKET sock, char *filename); //删除文件
    26 int ftp_renamefile(SOCKET sock, char *s, char *d); //修改文件/目录&移动文件/目录
    27 int ftp_server2local(SOCKET c_sock, char *s, char *d, int * size); //从服务器复制文件到本地 RETR
    28 int ftp_local2server(SOCKET c_sock, char *s, char *d, int * size); //从本地复制文件到服务器 STOR
    29 int ftp_recv(SOCKET sock, char *re_buf, ssize_t *len); //获取响应码
    30 
    31 
    32 #endif // FTPAPI_H_INCLUDED
    View Code

       下面这个是FTPResponseCode.h 文件 是对应答码简单的描述

     1 #ifndef FTPRESPONSECODE_H_INCLUDED
     2 #define FTPRESPONSECODE_H_INCLUDED
     3 
     4 
     5 #define FTP_SUCCESS 200  //成功
     6 #define FTP_SERVICE_READY 220 //服务器就绪
     7 #define FTP_LOGIN_SUCCESS 230 //登录因特网服务器
     8 #define FTP_FILE_ACTION_COMPLETE 250 //文件行为完成
     9 #define FTP_FILE_CREATED 257 //文件创建成功
    10 #define FTP_PASSWORD_REQUIREd 331 //要求密码
    11 #define FTP_LOGIN_PASSWORD_INCORRECT 530 //用户密码错误
    12 
    13 
    14 #endif // FTPRESPONSECODE_H_INCLUDED
    View Code

      下面这些是FTPAPI.cpp文件的函数代码

      创建一个socket连接并返回socket套接字 socket_connect

     1 /**
     2  * 作用: 创建一个Socket并返回.
     3  * 参数: IP或域名, 端口
     4  * 返回值: Socket套接字
     5  * */
     6 SOCKET socket_connect(char *host, int port)
     7 {
     8     int i=0;
     9     //初始化 Socket dll
    10     WSADATA wsaData;
    11     WORD socketVersion = MAKEWORD(2,0);
    12     if(WSAStartup(socketVersion, &wsaData))
    13     {
    14         printf("Init socket dll error!");
    15         exit(1);
    16     }
    17 
    18     struct hostent * server = gethostbyname(host);
    19     if(!server)
    20         return -1;
    21     unsigned char ch[4];
    22     char ip[20];
    23     //一个hostname 可以对应多个ip
    24     while(server->h_addr_list[i]!=NULL)
    25     {
    26         memcpy(&ch,server->h_addr_list[i],4);
    27         sprintf(ip,"%d.%d.%d.%d",ch[0],ch[1],ch[2],ch[3]);
    28         //printf("%s
    ",ip);
    29         i++;
    30     }
    31 
    32     //创建Socket
    33     SOCKET s = socket(AF_INET, SOCK_STREAM, 0); //TCP socket
    34     if(SOCKET_ERROR == s)
    35     {
    36         printf("Create Socket Error!");
    37         exit(1);
    38     }
    39     //设置超时连接
    40     int timeout = 3000; //复杂的网络环境要设置超时判断
    41     int ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
    42     ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout));
    43     //指定服务器地址
    44     struct sockaddr_in address;
    45     address.sin_family = AF_INET;
    46     address.sin_addr.S_un.S_addr = inet_addr(ip);
    47     address.sin_port = htons((unsigned short)port);
    48     //连接
    49     if(SOCKET_ERROR == connect(s,(LPSOCKADDR)&address,sizeof(address)))
    50     {
    51         printf("Can Not Connect To Server IP!
    ");
    52         exit(1);
    53     }
    54     return s;
    55 }
    View Code

      连接到一个ftp服务器 connect_server

     1 /**
     2  * 作用: 连接到一个FTP服务器,返回socket
     3  * 参数: IP或域名, 端口
     4  * 返回值: Socket套接字
     5  * */
     6 SOCKET connect_server(char *host, int port)
     7 {
     8     SOCKET ctrl_sock;
     9     char buf[BUFSIZE];
    10     int result;
    11     ssize_t len;
    12 
    13     ctrl_sock = socket_connect(host,port);
    14     if(-1 == ctrl_sock)
    15     {
    16         return -1;
    17     }
    18     while((len = recv(ctrl_sock, buf, BUFSIZE, 0)) > 0)
    19     {
    20         //len = recv(ctrl_sock, buf, BUFSIZE, 0);
    21         buf[len]=0;
    22         printf("%s
    ",buf); //220-FileZilla Server version 0.9.43 beta
    23     }
    24     sscanf(buf, "%d", &result);
    25 
    26     if(FTP_SERVICE_READY != result)
    27     {
    28         printf("FTP Not ready, Close the socet.");
    29         closesocket(ctrl_sock); //关闭Socket
    30         return -1;
    31     }
    32     return ctrl_sock;
    33 }
    View Code

      send发送命令,并返回recv结果 ftp_sendcmd_re

     1 /**
     2  * 作用: send发送命令,并返回recv结果
     3  * 参数: SOCKET,命令,命令返回码-命令返回描述,命令返回字节数
     4  * 返回值: 0 表示发送成功  -1表示发送失败
     5  * */
     6 int ftp_sendcmd_re(SOCKET sock, char *cmd, char *re_buf, ssize_t *len)
     7 {
     8     char buf[BUFSIZE];
     9     ssize_t r_len;
    10     if(send(sock, cmd, strlen(cmd), 0) == -1)
    11     {
    12         return -1;
    13     }
    14     r_len = recv(sock, buf, BUFSIZE, 0);
    15     if(r_len < 1)
    16         return -1;
    17     buf[r_len]=0;
    18     if(NULL != len)
    19         *len = r_len;
    20     if(NULL != re_buf)
    21         sprintf(re_buf, "%s", buf);
    22     return 0;
    23 }
    View Code

      send发送命令 ftp_sendcmd

     1 /**
     2  * 作用: send发送命令
     3  * 参数: SOCKET,命令
     4  * 返回值: FTP响应码
     5  * */
     6 int ftp_sendcmd(SOCKET sock, char *cmd)
     7 {
     8     char buf[BUFSIZE];
     9     int result;
    10     ssize_t len;
    11     printf("FTP Client: %s", cmd);
    12     result = ftp_sendcmd_re(sock, cmd, buf, &len);
    13     printf("FTP Server: %s", buf);
    14     if(0 == result)
    15     {
    16         sscanf(buf, "%d", &result);
    17     }
    18     return result;
    19 }
    View Code

      登录FTP服务器 login_server

     1 /**
     2  * 作用: 登录FTP服务器
     3  * 参数: SOCKET套接字,明文用户名,明文密码
     4  * 返回值: 0 表示登录成功   -1 表示登录失败
     5  * */
     6 int login_server(SOCKET sock, char *user, char *pwd)
     7 {
     8     char buf[BUFSIZE];
     9     int result;
    10     sprintf(buf, "USER %s
    ", user);
    11     //这里要对socket进行阻塞
    12     int timeout=0;
    13     setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout));
    14     result = ftp_sendcmd(sock, buf);
    15     if(FTP_LOGIN_SUCCESS == result) //直接登录
    16         return 0;
    17     else if(FTP_PASSWORD_REQUIREd == result) //需要密码
    18     {
    19         sprintf(buf, "PASS %s
    ", pwd);
    20         result = ftp_sendcmd(sock, buf);
    21         if(FTP_LOGIN_SUCCESS == result)
    22         {
    23             return 0;
    24         }
    25         else //530 密码错误
    26         {
    27             return -1;
    28         }
    29     }
    30     else
    31     {
    32         return -1;
    33     }
    34 }
    View Code

      winsock使用后,要调用WSACleanup函数关闭网络设备 socket_close

    1 /**
    2  * 作用: winsock使用后,要调用WSACleanup函数关闭网络设备,以便释放其占用的资源
    3  * 参数: SOCKET
    4  * 返回值: 无
    5  * */
    6 void socket_close(int c_sock)
    7 {
    8     WSACleanup();
    9 }
    View Code

      连接到FTP服务器 ftp_connect

     1 /**
     2  * 作用: 连接到FTP服务器
     3  * 参数: hostname或IP,端口,用户名,密码
     4  * 返回值: 已连接到FTP服务器的SOCKET   -1 表示登录失败
     5  * */
     6 SOCKET ftp_connect(char *host, int port, char *user, char *pwd)
     7 {
     8     SOCKET sock;
     9     sock = connect_server(host, port);
    10     if(-1 == sock)
    11     {
    12         return -1;
    13     }
    14     if(-1 == login_server(sock, user, pwd))
    15     {
    16         closesocket(sock);
    17         return -1;
    18     }
    19     return sock;
    20 }
    View Code

      断开FTP服务器 ftp_quit

     1 /**
     2  * 作用: 断开FTP服务器
     3  * 参数: SOCKET
     4  * 返回值: 成功断开状态码
     5  * */
     6 int ftp_quit(SOCKET sock)
     7 {
     8     int result = 0;
     9     result = ftp_sendcmd(sock, "QUIT
    ");
    10     closesocket(sock);
    11     socket_close(sock);
    12     return result;
    13 }
    View Code

      设置FTP传输类型 A:ascii I:Binary  ftp_type

     1 /**
     2  * 作用: 设置FTP传输类型 A:ascii I:Binary
     3  * 参数: SOCkET,类型
     4  * 返回值: 0 表示成功   -1 表示失败
     5  * */
     6 int ftp_type(SOCKET sock, char mode)
     7 {
     8     char buf[BUFSIZ];
     9     sprintf(buf,"TYPE %c
    ", mode);
    10     if(FTP_SUCCESS != ftp_sendcmd(sock, buf))
    11         return -1;
    12     else
    13         return 0;
    14 }
    View Code

      更改工作目录 ftp_cwd

     1 /**
     2  * 作用: 更改工作目录
     3  * 参数: SOCKET,工作目录
     4  * 返回值: 0 表示成功  -1 表示失败
     5  * */
     6 int ftp_cwd(SOCKET sock, char *path)
     7 {
     8     char buf[BUFSIZE];
     9     int result;
    10     sprintf(buf, "CWD %s
    ", path);
    11     result = ftp_sendcmd(sock, buf);
    12     if(FTP_FILE_ACTION_COMPLETE != result)  //250 文件行为完成
    13         return -1;
    14     else
    15         return 0;
    16 }
    View Code

      回到上级目录 ftp_cdup

     1 /**
     2  * 作用: 回到上级目录
     3  * 参数: SOCKET
     4  * 返回值: 0 正常操作返回  result 服务器返回响应码
     5  * */
     6 int ftp_cdup(SOCKET sock)
     7 {
     8     int result;
     9     result = ftp_sendcmd(sock, "CDUP
    ");
    10     if(FTP_FILE_ACTION_COMPLETE == result || FTP_SUCCESS == result)
    11         return 0;
    12     else
    13         return result;
    14 }
    View Code

      创建目录 ftp_mkd

     1 /**
     2  * 作用: 创建目录
     3  * 参数: SOCKET,文件目录路径(可相对路径,绝对路径)
     4  * 返回值: 0 正常操作返回  result 服务器返回响应码
     5  * */
     6 int ftp_mkd(SOCKET sock, char *path)
     7 {
     8     char buf[BUFSIZE];
     9     int result;
    10     sprintf(buf, "MKD %s
    ", path);
    11     result = ftp_sendcmd(sock, buf);
    12     if(FTP_FILE_CREATED != result) //257 路径名建立
    13         return result; //550 目录已存在
    14     else
    15         return 0;
    16 }
    View Code

      连接到PASV接口 ftp_pasv_connect

     1 /**
     2  * 作用: 连接到PASV接口
     3  *       PASV(被动)方式的连接过程是:
     4  *       客户端向服务器的FTP端口(默认是21)发送连接请求,
     5  *       服务器接受连接,建立一条命令链路。
     6  * 参数: 命令链路SOCKET cmd-socket
     7  * 返回值: 数据链路SOCKET raw-socket  -1 表示创建失败
     8  * */
     9 SOCKET ftp_pasv_connect(SOCKET c_sock)
    10 {
    11     SOCKET r_sock;
    12     int send_result;
    13     ssize_t len;
    14     int addr[6]; //IP*4+Port*2
    15     char buf[BUFSIZE];
    16     char result_buf[BUFSIZE];
    17 
    18     //设置PASV被动模式
    19     memset(buf,sizeof(buf),0);
    20     sprintf(buf, "PASV
    ");
    21     send_result = ftp_sendcmd_re(c_sock, buf, result_buf, &len);
    22     if(send_result == 0)
    23     {
    24         sscanf(result_buf, "%*[^(](%d,%d,%d,%d,%d,%d)",
    25                &addr[0],&addr[1],&addr[2],&addr[3],
    26                &addr[4],&addr[5]);
    27     }
    28 
    29     //连接PASV端口
    30     memset(buf, sizeof(buf), 0);
    31     sprintf(buf, "%d.%d.%d.%d",addr[0],addr[1],addr[2],addr[3]);
    32     r_sock = socket_connect(buf,addr[4]*256+addr[5]);
    33     if(-1 == r_sock)
    34         return -1;
    35     return r_sock;
    36 }
    View Code

      列出FTP工作空间的所有目录 ftp_list

     1 /**
     2  * 作用: 列出FTP工作空间的所有目录
     3  * 参数: 命令链路SOCKET,工作空间,列表信息,列表信息大小
     4  * 返回值: 0 表示列表成功  result>0 表示其他错误响应码  -1 表示创建pasv错误
     5  * */
     6 int ftp_list(SOCKET c_sock, char *path, char **data, int *data_len)
     7 {
     8     SOCKET r_sock;
     9     char buf[BUFSIZE];
    10     int send_re;
    11     int result;
    12     ssize_t len,buf_len,total_len;
    13 
    14     //连接到PASV接口
    15     r_sock = ftp_pasv_connect(c_sock);
    16     if(-1 == r_sock)
    17     {
    18         return -1;
    19     }
    20     //发送LIST命令
    21     memset(buf,sizeof(buf),0);
    22     sprintf(buf, "LIST %s
    ", path);
    23     send_re = ftp_sendcmd(c_sock, buf);
    24     if(send_re >= 300 || send_re == 0)
    25         return send_re;
    26     len=total_len=0;
    27     buf_len=BUFSIZE;
    28     char *re_buf = (char *)malloc(buf_len);
    29     while( (len = recv(r_sock,buf,BUFSIZE,0)) > 0)
    30     {
    31         if(total_len+len > buf_len)
    32         {
    33             buf_len *= 2;
    34             char *re_buf_n = (char *)malloc(buf_len);
    35             memcpy(re_buf_n, re_buf, total_len);
    36             free(re_buf);
    37             re_buf = re_buf_n;
    38         }
    39         memcpy(re_buf+total_len, buf, len);
    40         total_len += len;
    41     }
    42     closesocket(r_sock);
    43 
    44     //向服务器接收返回值
    45     memset(buf, sizeof(buf), 0);
    46     len = recv(c_sock, buf, BUFSIZE, 0);
    47     buf[len] = 0;
    48     sscanf(buf, "%d", &result);
    49     if(result != 226)
    50     {
    51         free(re_buf);
    52         return result;
    53     }
    54     *data = re_buf;
    55     *data_len = total_len;
    56     return 0;
    57 }
    View Code

      删除目录 ftp_deletefolder

     1 /**
     2  * 作用: 删除目录
     3  * 参数: 命令链路SOCKET,路径目录
     4  * 返回值: 0 表示列表成功  result>0 表示其他错误响应码
     5  * */
     6 int ftp_deletefolder(SOCKET sock, char *path)
     7 {
     8     char buf[BUFSIZE];
     9     int result;
    10     sprintf(buf,"RMD %s
    ", path);
    11     result = ftp_sendcmd(sock, buf);
    12     if(FTP_FILE_ACTION_COMPLETE != result)
    13     {
    14         //550 Directory not empty.
    15         //550 Directory not found.
    16         return result;
    17     }
    18     return 0;
    19 }
    View Code

      删除文件 ftp_deletefile

     1 /**
     2  * 作用: 删除文件
     3  * 参数: 命令链路SOCKET,路径文件(相对/绝对)
     4  * 返回值: 0 表示列表成功  result>0 表示其他错误响应码
     5  * */
     6 int ftp_deletefile(SOCKET sock, char *filename)
     7 {
     8     char buf[BUFSIZE];
     9     int result;
    10     sprintf(buf, "DELE %s
    ", filename);
    11     result = ftp_sendcmd(sock, buf);
    12     if(FTP_FILE_ACTION_COMPLETE != 250) //250 File deleted successfully
    13     {
    14         //550 File not found.
    15         return result;
    16     }
    17     return 0;
    18 }
    View Code

      修改文件名&移动目录 ftp_renamefile

     1 /**
     2  * 作用: 修改文件名&移动目录
     3  * 参数: 命令链路SOCKET,源地址,目的地址
     4  * 返回值: 0 表示列表成功  result>0 表示其他错误响应码
     5  * */
     6 int ftp_renamefile(SOCKET sock, char *s, char *d)
     7 {
     8     char buf[BUFSIZE];
     9     int result;
    10     sprintf(buf, "RNFR %s
    ", s);
    11     result = ftp_sendcmd(sock, buf);
    12     if(350 != result) //350 文件行为暂停,因为要进行移动操作
    13         return result;
    14     sprintf(buf, "RNTO %s
    ", d);
    15     result = ftp_sendcmd(sock, buf);
    16     if(FTP_FILE_ACTION_COMPLETE != result)
    17     {
    18         return result;
    19     }
    20     return 0;
    21 }
    View Code

      从服务器复制文件到本地 RETR  ftp_server2local

     1 /**
     2  * 作用: 从服务器复制文件到本地 RETR
     3  * 参数: SOCKET,源地址,目的地址,文件大小
     4  * 返回值: 0 表示列表成功  result>0 表示其他错误响应码
     5  *         -1:文件创建失败  -2 pasv接口错误
     6  * */
     7 int ftp_server2local(SOCKET c_sock, char *s, char *d, int * size)
     8 {
     9     SOCKET d_sock;
    10     ssize_t len,write_len;
    11     char buf[BUFSIZ];
    12     int result;
    13     *size=0;
    14     //打开本地文件
    15     FILE * fp = fopen(d, "wb");
    16     if(NULL == fp)
    17     {
    18         printf("Can't Open the file.
    ");
    19         return -1;
    20     }
    21     //设置传输模式
    22     ftp_type(c_sock,'I');
    23 
    24     //连接到PASV接口 用于传输文件
    25     d_sock = ftp_pasv_connect(c_sock);
    26     if(-1 == d_sock)
    27     {
    28         fclose(fp); //关闭文件
    29         return -2;
    30     }
    31 
    32     //发送RETR命令
    33     memset(buf, sizeof(buf), 0);
    34     sprintf(buf, "RETR %s
    ", s);
    35     result = ftp_sendcmd(c_sock, buf);
    36     // 150 Opening data channel for file download from server of "xxxx"
    37     if(result >= 300 || result == 0) //失败可能是没有权限什么的,具体看响应码
    38     {
    39         fclose(fp);
    40         return result;
    41     }
    42 
    43     //开始向PASV读取数据(下载)
    44     memset(buf, sizeof(buf), 0);
    45     while((len = recv(d_sock, buf, BUFSIZE, 0)) > 0 )
    46     {
    47         write_len = fwrite(&buf, len, 1, fp);
    48         if(write_len != 1) //写入文件不完整
    49         {
    50             closesocket(d_sock); //关闭套接字
    51             fclose(fp); //关闭文件
    52             return -1;
    53         }
    54         if(NULL != size)
    55         {
    56             *size += write_len;
    57         }
    58     }
    59     //下载完成
    60     closesocket(d_sock);
    61     fclose(fp);
    62 
    63     //向服务器接收返回值
    64     memset(buf, sizeof(buf), 0);
    65     len = recv(c_sock, buf, BUFSIZE, 0);
    66     buf[len] = 0;
    67     printf("%s
    ",buf);
    68     sscanf(buf, "%d", &result);
    69     if(result >= 300)
    70     {
    71         return result;
    72     }
    73     //226 Successfully transferred "xxxx"
    74     return 0;
    75 }
    View Code

      从本地复制文件到服务器 STOR ftp_local2server

     1 /**
     2  * 作用: 从本地复制文件到服务器 STOR
     3  * 参数: SOCKET,源地址,目的地址,文件大小
     4  * 返回值: 0 表示列表成功  result>0 表示其他错误响应码
     5  *         -1:文件创建失败  -2 pasv接口错误
     6  * */
     7 int ftp_local2server(SOCKET c_sock, char *s, char *d, int * size)
     8 {
     9     SOCKET d_sock;
    10     ssize_t len,send_len;
    11     char buf[BUFSIZE];
    12     FILE * fp;
    13     int send_re;
    14     int result;
    15     //打开本地文件
    16     fp = fopen(s, "rb");
    17     if(NULL == fp)
    18     {
    19         printf("Can't Not Open the file.
    ");
    20         return -1;
    21     }
    22     //设置传输模式
    23     ftp_type(c_sock, 'I');
    24     //连接到PASV接口
    25     d_sock = ftp_pasv_connect(c_sock);
    26     if(d_sock == -1)
    27     {
    28         fclose(fp);
    29         return -1;
    30     }
    31 
    32     //发送STOR命令
    33     memset(buf, sizeof(buf), 0);
    34     sprintf(buf, "STOR %s
    ", d);
    35     send_re = ftp_sendcmd(c_sock, buf);
    36     if(send_re >= 300 || send_re == 0)
    37     {
    38         fclose(fp);
    39         return send_re;
    40     }
    41 
    42     //开始向PASV通道写数据
    43     memset(buf, sizeof(buf), 0);
    44     while( (len = fread(buf, 1, BUFSIZE, fp)) > 0)
    45     {
    46         send_len = send(d_sock, buf, len, 0);
    47         if(send_len != len)
    48         {
    49             closesocket(d_sock);
    50             fclose(fp);
    51             return -1;
    52         }
    53         if(NULL != size)
    54         {
    55             *size += send_len;
    56         }
    57     }
    58     //完成上传
    59     closesocket(d_sock);
    60     fclose(fp);
    61 
    62     //向服务器接收响应码
    63     memset(buf, sizeof(buf), 0);
    64     len = recv(c_sock, buf, BUFSIZE, 0);
    65     buf[len] = 0;
    66     sscanf(buf, "%d", &result);
    67     if(result >= 300)
    68     {
    69         return result;
    70     }
    71     return 0;
    72 }
    View Code

      获取一行响应码 ftp_recv

     1 /**
     2  * 作用: 获取一行响应码
     3  * 参数:
     4  * 返回值:
     5  * */
     6 int ftp_recv(SOCKET sock, char *re_buf, ssize_t *len)
     7 {
     8     char buf[BUFSIZE];
     9     ssize_t r_len;
    10     int timeout = 3000;
    11     setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
    12     r_len = recv(sock, buf, BUFSIZE, 0);
    13     timeout = 0;
    14     setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
    15     if(r_len < 1)
    16         return -1;
    17     buf[r_len]=0;
    18     if(NULL != len)
    19         *len = r_len;
    20     if(NULL != re_buf)
    21         sprintf(re_buf, "%s", buf);
    22     return 0;
    23 }
    View Code

     4.测试ftp客户端

      下载文件到本地

    1     SOCKET s = ftp_connect("127.0.0.1",21,"user","user"); //登录到FTP服务器
    2     int ret = ftp_server2local(s,"user/user.zip","bin.zip",&size); //在FTP服务器获取文件
    3     ftp_quit(s); //退出FTP服务器

      上传文件到服务器

    1     SOCKET s = ftp_connect("127.0.0.1",21,"user","user"); //登录到FTP服务器
    2     int ret = ftp_local2server(s,"user/user.zip","bin.zip",&size); //发送文件到FTP服务器
    3     ftp_quit(s); //退出FTP服务器

      下面这个是服务器的日志信息

      

      下面这个是程序打印的调试信息

       

    5.后话

      到这里这个简单的ftp库就可以实现绝大部分的客户端功能了,但是这里面有一个问题,就是ftp是明文传输用户名/密码的,如果ftp上的文件比较重要的话,那么就有点问题了。当然这个不是本次的关注点,本次主要是了解ftp协议,还有从代码中了解这种交换控制命令的方法是一种很不错的技术手段,虽然这种方法已经是好多年前的,不安全,也过时了。但还是有可学的地方。

    6.附录

      下面这个附录是利用wireshark进行本地网络抓包测试。

    1、抓包,要看部署点,在路由器、交换机等设备上做端口镜像、或分光口,或是接HUB、TAP等设备就可以直接获得通过这些口的报文。
    2、抓包,也可在以局域网部署相关的网管软件或黑客工具(比如cain),可以用arp骗方式,让你的数据先发送到监控机上,然后再转发走。。这样你的数据就。。

    建议:
    1、建议在电脑上打开ARP防护功能
    2、在使用中尽量使用加密传输的工具,比如SSH、SSL、QQ一类的东西。可避免一些危害.

      注意wireshark是不能抓取本地回环地址的数据包的,所以我以远程ftp服务器进行测试

       

      这里是通过浏览器进行连接的。wireshark 1.12.4 从上面可以看到的信息 29-44这些表示了,浏览器一开始使用匿名进行登录,发现登录不上,所以请求用户名登录在81 82 84 85这4行中我们可以分析到,我是输入用户名user 密码user进行登录的,第106行表示用户名/密码错误。 如果是230 Login in 就表示成功登录了。如果我们捉到了这些信息,那么我们就可以进行登录了。这样就不安全了。既然ftp这么不安全为什么那么多地方用到ftp共享文件。这个就要说到ftp的作用了,ftp作用本来就是共享文件,所以安全性就不是很重要了。 至于加密方式以后再讲。

       (开发环境mignw 编译的时候要加入libws2_32.a 这个库, 编译命令 g++ ftpapi.cpp -c -o ftpapi.o -lws2_32)

      参考资料

      TanHao的 THFTPAPI.c 文件 http://www.tanhao.me

      文件下载 ftpapi.zip http://files.cnblogs.com/files/wunaozai/ftpapi20150512.zip 

  • 相关阅读:
    记一次 css样式的使用
    Vue -- element
    Ubuntu系统 ,鼠标双击搜索框内容 ,内容会被自动删除
    MetInfo Password Reset Poisoning By Host Header Attack
    wechall writeup
    SQL常用语句
    PHP Tips
    PHP版本差异备忘录
    Docker入门
    JetBrains系列软件用法
  • 原文地址:https://www.cnblogs.com/wunaozai/p/4494536.html
Copyright © 2011-2022 走看看