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

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

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

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

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

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

  连接到一个ftp服务器 connect_server

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

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

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

  send发送命令 ftp_sendcmd

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

  登录FTP服务器 login_server

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

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

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

  连接到FTP服务器 ftp_connect

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

  断开FTP服务器 ftp_quit

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

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

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

  更改工作目录 ftp_cwd

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

  回到上级目录 ftp_cdup

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

  创建目录 ftp_mkd

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

  连接到PASV接口 ftp_pasv_connect

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

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

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

  删除目录 ftp_deletefolder

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

  删除文件 ftp_deletefile

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

  修改文件名&移动目录 ftp_renamefile

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

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

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

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

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

  获取一行响应码 ftp_recv

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

4.测试ftp客户端

  下载文件到本地

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

  上传文件到服务器

  1. 1 SOCKET s = ftp_connect("127.0.0.1",21,"user","user"); //登录到FTP服务器
  2. 2 int ret = ftp_local2server(s,"user/user.zip","bin.zip",&size); //发送文件到FTP服务器
  3. 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

作者:无脑仔的小明
出处:http://www.cnblogs.com/wunaozai/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如果文中有什么错误,欢迎指出。以免更多的人被误导。
 

Socket网络编程--FTP客户端的更多相关文章

  1. Socket网络编程--FTP客户端(1)(Windows)

    已经好久没有写过博客进行分享了.具体原因,在以后说. 这几天在了解FTP协议,准备任务是写一个FTP客户端程序.直接上干货了. 0.了解FTP作用 就是一个提供一个文件的共享协议. 1.了解FTP协议 ...

  2. Socket网络编程--FTP客户端(60篇socket博客,而且都比较简单、深入浅出)

    已经好久没有写过博客进行分享了.具体原因,在以后说. 这几天在了解FTP协议,准备任务是写一个FTP客户端程序.直接上干货了. 0.了解FTP作用 就是一个提供一个文件的共享协议. 1.了解FTP协议 ...

  3. Socket网络编程--FTP客户端(2)(Windows)

    上一篇FTP客户端讲到如果制作一个简单的FTP客户端,功能实现了,但是后面我们发现了问题,就是FTP是使用明文进行操作的.对于普通情况来说就无所谓了.但有时候要安全的一点的话,就应该使用FTP的安全版 ...

  4. 基于Socket网络编程

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/a2011480169/article/details/73602708 博客核心内容: 1.Sock ...

  5. 循序渐进Socket网络编程(多客户端、信息共享、文件传输)

    循序渐进Socket网络编程(多客户端.信息共享.文件传输) 前言:在最近一个即将结束的项目中使用到了Socket编程,用于调用另一系统进行处理并返回数据.故把Socket的基础知识总结梳理一遍. 1 ...

  6. Python Socket 网络编程 (客户端的编程)

    Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...

  7. Python全栈【Socket网络编程】

    Python全栈[socket网络编程] 本章内容: Socket 基于TCP的套接字 基于UDP的套接字 TCP粘包 SocketServer 模块(ThreadingTCPServer源码剖析) ...

  8. Socket网络编程-基础篇

    Socket网络编程 网络通讯三要素: IP地址[主机名] 网络中设备的标识 本地回环地址:127.0.0.1 主机名:localhost 端口号 用于标识进程的逻辑地址 有效端口:0~65535 其 ...

  9. Socket网络编程详解

    一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...

随机推荐

  1. ipcs, ipcrm

    ipcs ipcs -m #查看系统中已经存在的共享内存 ------ Shared Memory Segments -------- key shmid owner perms bytes natt ...

  2. CentOS系统在不重启的情况下为虚拟机添加新硬盘

    一.概述 用过虚拟机的都知道,如果在系统运行的时候去给虚拟机添加一块新设备,比如说硬盘,系统是读取不到这个新硬盘的,因为系统在启动的时候会去检测硬件设备.但是我们也可能会遇到这样的情况,比如正在运行比 ...

  3. linux权限补充:rwt rwT rws rwS 特殊权限

    众所周知,Linux的文件权限如: 777:666等,其实只要在相应的文件上加上UID的权限,就可以用到加权限人的身份去运行这个文件.所以我们只需要将bash复制出来到另一个地方,然后用root加上U ...

  4. 利用apply和arguments复用方法

    首先,有个单例对象,它上面挂了很多静态工具方法.其中有一个是each,用来遍历数组或对象. var nativeForEach = [].forEach var nativeMap = [].map ...

  5. oracle--trunc与to_char的区别

    trunc取得是天(可比较),而to_char取得是数值(可计算): 但trunc(date) 具有与to_char(date) 相似的功能,但有区别:   trunc(sysdate,'cc')  ...

  6. Visual Studio 中的快捷键

    VS中常用快捷键 Ctrl+K+D:快速对齐代码 Ctrl+E+D:快速对齐代码 Ctrl+Z:撤销 Ctrl+S:保存 Ctrl+Shift+S:保存所有 Ctrl+J:快速弹出智能提示 Shift ...

  7. GDB教程详解

    GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具.或许,各位比较喜欢那种图形界面方式的,像VC.BCB等IDE的调试,但如果你是在UNIX平台下做软件,你会发现GDB这个调试工具有比VC ...

  8. IIS中查看W3P.exe进程对应的应用程序池的方法

    对于IIS6可以运行 iisapp -a来显示应用程序池的性能列表. iisapp.vbs它是一个脚本程序,在安装2003时需要SP1才有 而IIS7可以直接用它的外壳命令 C:"Windo ...

  9. [译] libvirt 虚机的生命周期 (Libvirt Virtual Machine Lifecycle)

    翻译自:http://wiki.libvirt.org/page/VM_lifecycle   这篇文章描述虚机生命周期的基本概念.其目的在于在一篇文章中提供完整的关于虚机创建.运行.停止.迁移和删除 ...

  10. C++11 之 scoped enum

      C++11 枚举类型是“域化的” (scoped enum),相比 C++98 枚举类型的“非域化” (unscoped enum),具有如下优点: 1  命名空间污染  一般来说,声明在花括号内 ...