使用c++实现一个FTP客户端(三)

接上篇:http://www.cnblogs.com/jzincnblogs/p/5217688.html,这篇主要记录编程过程中需要注意的地方以及遇到的一些问题及解决方法。

  一、gethostbyname(),inet_ntoa()等函数已经过时

    使用上面两个函数时编译器会报错并提示函数已经是过时的了(obsolete),应该用getaddrinfo()与InetNtop()代替,这两个函数都是协议无关的,同时支持IPv4和IPv6,下面是一个使用例子:

复制代码
 1 string GetIPAddress(int af)
 2 {
 3     char host_name[IP_SIZE];
 4     char buf_ip[IP_SIZE];
 5     //
 6     addrinfo hints;
 7     memset(&hints, 0, sizeof(addrinfo));
 8     hints.ai_family = af;
 9     hints.ai_socktype = SOCK_STREAM;
10     hints.ai_protocol = IPPROTO_TCP;
11     //
12     addrinfo *result = nullptr;
13     //获取主机名字
14     int ret_val = ::gethostname(host_name, IP_SIZE);
15     if (ret_val == SOCKET_ERROR)
16     {
17         cerr << "Failed to get host name!\n";
18         return "";
19     }
20     //通过主机名字获取ip地址
21     ret_val = ::getaddrinfo(host_name, nullptr, &hints, &result);
22     if (ret_val != 0)
23     {
24         cerr << "Failed tp get host by name!\n";
25         return "";
26     }
27     SOCKADDR_IN *addr = (SOCKADDR_IN*)result->ai_addr;
28     ::InetNtop(af, &addr->sin_addr, buf_ip, IP_SIZE);
29     //释放地址资源
30     ::freeaddrinfo(result);
31     return (string)buf_ip;
32 }
复制代码

    关于两个函数的典型用法可以参考MSDN:https://msdn.microsoft.com/en-us/library/ms738520(v=vs.85).aspx

                       https://msdn.microsoft.com/en-us/library/cc805843(v=vs.85).aspx

 

  二、换行符的问题

    c++中如果输出时需要换行可以使用\n,但需要注意的是,在windows中回车换行表示为\r\n,而linux中表示为\n,而这也是FTP协议中二进制模式与ASCII模式的区别之一:ASCII模式会对文件进行转换,将换行符转换为客户端系统的表示方法,而二进制模式则不对文件进行改动。所以在windows环境下,FTP客户端与服务器交互过程中,客户端发送命令时要以\r\n结尾,而接收服务器的多行数据时每行数据的换行符均为\r\n。

 

  三、被动模式

    在FTP客户端与服务器进行数据传输时,一般使用被动模式,而客户端与服务器的数据连接在每次传输完成后都会关闭,这意味着每次客户端与服务器传输数据前都要先建立数据连接,也就意味着每次都要重新进入被动模式。通过发送PASV命令可以请求进入被动模式,若进入成功,服务器返回一条形如 227 Entering Passive Mode (a,b,c,d,e,f). 的消息,其中a.b.c.d表示服务器的IP地址,通过e,f可计算得到客户端应连接的服务器端口号,计算公式为:端口号=e*256 + f。

 

  四、断点续传

    当客户端下载文件过程因种种原因中断后,下次启动下载时就要用到断点续传,避免重新下载。

    断点续传的实现步骤如下:

      1.调用Windows API函数CreateFile()打开文件,然后使用GetFileSize()获取已下载的字节数。

      2.从服务器中获取目标文件的字节数,进行比较。

      3.断点续传的开始位置为已下载的字节数加1。

      4.发送命令“REST offset\r\n”,其中offset为计算出来的文件偏移量。

      5.若服务器响应成功,则发送命令“RETR 文件名\r\n”,若响应成功,文件开始断点续传。

    断点续传关键部分代码如下:

复制代码
 1         HANDLE h_file = ::CreateFile(str_path.c_str(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
 2         if (h_file == INVALID_HANDLE_VALUE)
 3         {
 4             return false;
 5         }
 6         int dld_size = ::GetFileSize(h_file, nullptr);
 7         ::CloseHandle(h_file);
 8         //
 9         int file_size = GetFileInfo(f).GetSize();
10         if (file_size == dld_size)
11         {
12             cout << "File already downloaded!\n";
13             return false;
14         }
15         //
16         int read_start = dld_size + 1;
17         file.open(str_path, fstream::out | fstream::app);
18         //
19         char buf_num[32];
20         memset(buf_num, 0, sizeof(buf_num));
21         _itoa_s(read_start, buf_num, sizeof(buf_num), 10);
22         string cmd_dld = "REST ";
23         cmd_dld += buf_num;
24         cmd_dld += "\r\n";
25         //
26         EnterPasvMode();
27         //
28         logger.SendCmd(cmd_dld);
29         logger.RecvResponse();
30         if (logger.GetLastLog().substr(0, 3) == "500")
31         {
32             cerr << "File name incorrect!\n";
33             return false;
34         }
35         //
36         cmd_dld = "RETR " + f + "\r\n";
37         logger.SendCmd(cmd_dld);
38         logger.RecvResponse();
39         //
40         while (::recv(sock_data, dld_file, FILE_SIZE, 0) != 0)
41         {
42             cout << strlen(dld_file) << "\n";
43             file << dld_file;
44             memset(dld_file, 0, FILE_SIZE);
45         }
46         file.close();
47         sock_data.Close();
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FTP(File Transfer Protocol)是一种常用的文件传输协议,它允许用户在网络上通过 FTP 客户端FTP 服务器之间传输文件。 下面是一个简单的 C 实现 FTP 客户端的代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #define BUF_SIZE 1024 int main(int argc, char *argv[]) { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); exit(1); } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(21); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("connect"); exit(1); } char buffer[BUF_SIZE]; int recv_len; recv_len = recv(sock, buffer, BUF_SIZE-1, 0); if (recv_len == -1) { perror("recv"); exit(1); } buffer[recv_len] = '\0'; printf("%s", buffer); char user[] = "USER anonymous\r\n"; if (send(sock, user, strlen(user), 0) == -1) { perror("send"); exit(1); } recv_len = recv(sock, buffer, BUF_SIZE-1, 0); if (recv_len == -1) { perror("recv"); exit(1); } buffer[recv_len] = '\0'; printf("%s", buffer); char pass[] = "PASS anonymous\r\n"; if (send(sock, pass, strlen(pass), 0) == -1) { perror("send"); exit(1); } recv_len = recv(sock, buffer, BUF_SIZE-1, 0); if (recv_len == -1) { perror("recv"); exit(1); } buffer[recv_len] = '\0'; printf("%s", buffer); char pwd[] = "PWD\r\n"; if (send(sock, pwd, strlen(pwd), 0) == -1) { perror("send"); exit(1); } recv_len = recv(sock, buffer, BUF_SIZE-1, 0); if (recv_len == -1) { perror("recv"); exit(1); } buffer[recv_len] = '\0'; printf("%s", buffer); close(sock); return 0; } ``` 这段代码实现FTP 客户端的基本功能,包括连接 FTP 服务器、登录、获取当前目录等。具体来说,它的实现步骤如下: 1. 创建一个 TCP 套接字; 2. 设置 FTP 服务器的地址和端口号; 3. 连接 FTP 服务器; 4. 接收 FTP 服务器的欢迎信息; 5. 发送用户名和密码,进行登录; 6. 接收登录成功信息; 7. 发送命令 PWD,获取当前目录; 8. 接收当前目录信息; 9. 关闭套接字。 在实际应用中,我们可以根据需要扩展这段代码,实现更多 FTP 客户端的功能,比如上传、下载、删除等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值