FTP 客户端C实现

2023-05-16

使用 Socket 通信实现 FTP 客户端程序

FTP 概述

文件传输协议(FTP)作为网络共享文件的传输协议,在网络应用软件中具有广泛的应用。FTP的目标是提高文件的共享性和可靠高效地传送数据。

在传输文件时,FTP 客户端程序先与服务器建立连接,然后向服务器发送命令。服务器收到命令后给予响应,并执行命令。FTP 协议与操作系统无关,任何操作系统上的程序只要符合 FTP 协议,就可以相互传输数据。本文主要基于 LINUX 平台,对 FTP 客户端的实现原理进行详尽的解释并阐述如何使用 C 语言编写一个简单的 FTP 客户端。

FTP 协议

相比其他协议,如 HTTP 协议,FTP 协议要复杂一些。与一般的 C/S 应用不同点在于一般的C/S 应用程序一般只会建立一个 Socket 连接,这个连接同时处理服务器端和客户端的连接命令和数据传输。而FTP协议中将命令与数据分开传送的方法提高了效率。

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

命令端口

一般来说,客户端有一个 Socket 用来连接 FTP 服务器的相关端口,它负责 FTP 命令的发送和接收返回的响应信息。一些操作如“登录”、“改变目录”、“删除文件”,依靠这个连接发送命令就可完成。

数据端口

对于有数据传输的操作,主要是显示目录列表,上传、下载文件,我们需要依靠另一个 Socket来完成。

如果使用被动模式,通常服务器端会返回一个端口号。客户端需要用另开一个 Socket 来连接这个端口,然后我们可根据操作来发送命令,数据会通过新开的一个端口传输。

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

下面对 FTP 的主动模式和被动模式做一个简单的介绍。

主动模式 (PORT)

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

FTP 的客户端只是告诉服务器自己的端口号,让服务器来连接客户端指定的端口。对于客户端的防火墙来说,这是从外部到内部的连接,可能会被阻塞。

被动模式 (PASV)

为了解决服务器发起到客户的连接问题,有了另一种 FTP 连接方式,即被动方式。命令连接和数据连接都由客户端发起,这样就解决了从服务器到客户端的数据端口的连接被防火墙过滤的问题。

被动模式下,当开启一个 FTP 连接时,客户端打开两个任意的本地端口 (N > 1024 和 N+1) 。

第一个端口连接服务器的 21 端口,提交 PASV 命令。然后,服务器会开启一个任意的端口 (P > 1024 ),返回如“227 entering passive mode (127,0,0,1,4,18)”。 它返回了 227 开头的信息,在括号中有以逗号隔开的六个数字,前四个指服务器的地址,最后两个,将倒数第二个乘 256 再加上最后一个数字,这就是 FTP 服务器开放的用来进行数据传输的端口。如得到 227 entering passive mode (h1,h2,h3,h4,p1,p2),那么端口号是 p1*256+p2,ip 地址为h1.h2.h3.h4。这意味着在服务器上有一个端口被开放。客户端收到命令取得端口号之后, 会通过 N+1 号端口连接服务器的端口 P,然后在两个端口之间进行数据传输。

主要用到的 FTP 命令

FTP 每个命令都有 3 到 4 个字母组成,命令后面跟参数,用空格分开。每个命令都以 "\r\n"结束。

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

USER: 指定用户名。通常是控制连接后第一个发出的命令。“USER gaoleyi\r\n”: 用户名为gaoleyi 登录。

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

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

CWD: 改变工作目录。如:“CWD dirname\r\n”。

PASV: 让服务器在数据端口监听,进入被动模式。如:“PASV\r\n”。

PORT: 告诉 FTP 服务器客户端监听的端口号,让 FTP 服务器采用主动模式连接客户端。如:“PORT h1,h2,h3,h4,p1,p2”。

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

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

REST: 该命令并不传送文件,而是略过指定点后的数据。此命令后应该跟其它要求文件传输的 FTP 命令。“REST 100\r\n”:重新指定文件传送的偏移量为 100 字节。

QUIT: 关闭与服务器的连接。

FTP 响应码

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

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

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

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

第三个数字提供了更加详细的信息。

第一个数字的含义如下:

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

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

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

4 表示信息暂时错误。

5 表示信息永久错误。

第二个数字的含义如下:

0 表示语法。

1 表示系统状态和信息。

2 表示连接状态。

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

4 表示未定义。

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

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

实现 FTP 客户端上传下载功能

下面让我们通过一个例子来对 FTP 客户端有一个深入的了解。本文实现的 FTP 客户端有下列功能:

  1. 客户端和 FTP 服务器建立 Socket 连接。
  2. 向服务器发送 USER、PASS 命令登录 FTP 服务器。
  3. 使用 PASV 命令得到服务器监听的端口号,建立数据连接。
  4. 使用 RETR/STOR 命令下载/上传文件。
  5. 在下载完毕后断开数据连接并发送 QUIT 命令退出。

本例中使用的 FTP 服务器为 filezilla。在整个交互的过程中,控制连接始终处于连接的状态,数据连接在每传输一个文件时先打开,后关闭。

客户端和 FTP 服务器建立 Socket 连接

当客户端与服务器建立连接后,服务器会返回 220 的响应码和一些欢迎信息。

图 1. 客户端连接到服务器端

图 1. 客户端连接到服务器端

清单 1. 客户端连接到 FTP 服务器,接收欢迎信息

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

SOCKET control_sock;

struct hostent *hp;

struct sockaddr_in server;

memset(&server, 0, sizeof(struct sockaddr_in));

 

/* 初始化socket */

control_sock = socket(AF_INET, SOCK_STREAM, 0);

hp = gethostbyname(server_name);

memcpy(&server.sin_addr, hp->h_addr, hp->h_length);

server.sin_family = AF_INET;

server.sin_port = htons(port);

 

/* 连接到服务器端 */

connect(control_sock,(struct sockaddr *)&server, sizeof(server));

/* 客户端接收服务器端的一些欢迎信息 */

read(control_sock, read_buf, read_len);

客户端登录 FTP 服务器

当客户端发送用户名和密码,服务器验证通过后,会返回 230 的响应码。然后客户端就可以向服务器端发送命令了。

图 2. 客户端登录 FTP 服务器

客户端登录 FTP 服务器

清单 2. 客户端发送用户名和密码,登入 FTP 服务器

1

2

3

4

5

6

7

8

9

10

11

12

13

/* 命令 ”USER username\r\n” */

sprintf(send_buf,"USER %s\r\n",username);

/*客户端发送用户名到服务器端 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”331 User name okay, need password.” */

read(control_sock, read_buf, read_len);

 

/* 命令 ”PASS password\r\n” */

sprintf(send_buf,"PASS %s\r\n",password);

/* 客户端发送密码到服务器端 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”230 User logged in, proceed.” */

read(control_sock, read_buf, read_len);

客户端让 FTP 服务器进入被动模式

当客户端在下载/上传文件前,要先发送命令让服务器进入被动模式。服务器会打开数据端口并监听。并返回响应码 227 和数据连接的端口号。

图 3. 客户端让服务器进入被动模式

图 3. 客户端让服务器进入被动模式

清单 3. 让服务器进入被动模式,在数据端口监听

1

2

3

4

5

6

7

/* 命令 ”PASV\r\n” */

sprintf(send_buf,"PASV\r\n");

/* 客户端告诉服务器用被动模式 */

write(control_sock, send_buf, strlen(send_buf));

/*客户端接收服务器的响应码和新开的端口号,

* 正常为 ”227 Entering passive mode (<h1,h2,h3,h4,p1,p2>)” */

read(control_sock, read_buf, read_len);

客户端通过被动模式下载文件

当客户端发送命令下载文件。服务器会返回响应码 150,并向数据连接发送文件内容。

图 4. 客户端从FTP服务器端下载文件

图 4. 客户端从FTP服务器端下载文件

清单 4. 客户端连接到 FTP 服务器的数据端口并下载文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

/* 连接服务器新开的数据端口 */

connect(data_sock,(struct sockaddr *)&server, sizeof(server));

/* 命令 ”CWD dirname\r\n” */

sprintf(send_buf,"CWD %s\r\n", dirname);

/* 客户端发送命令改变工作目录 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”250 Command okay.” */

read(control_sock, read_buf, read_len);

 

/* 命令 ”SIZE filename\r\n” */

sprintf(send_buf,"SIZE %s\r\n",filename);

/* 客户端发送命令从服务器端得到下载文件的大小 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”213 <size>” */

read(control_sock, read_buf, read_len);

 

/* 命令 ”RETR filename\r\n” */

sprintf(send_buf,"RETR %s\r\n",filename);

/* 客户端发送命令从服务器端下载文件 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”150 Opening data connection.” */

read(control_sock, read_buf, read_len);

 

/* 客户端创建文件 */

file_handle = open(disk_name, CRFLAGS, RWXALL);

for( ; ; ) {

... ...

/* 客户端通过数据连接 从服务器接收文件内容 */

read(data_sock, read_buf, read_len);

/* 客户端写文件 */

write(file_handle, read_buf, read_len);

... ...

}

/* 客户端关闭文件 */

rc = close(file_handle);

客户端退出服务器

当客户端下载完毕后,发送命令退出服务器,并关闭连接。服务器会返回响应码 200。

图 5. 客户端从 FTP 服务器退出

图 5. 客户端从 FTP 服务器退出

清单 5. 客户端关闭数据连接,退出 FTP 服务器并关闭控制连接

1

2

3

4

5

6

7

8

9

10

11

12

13

/* 客户端关闭数据连接 */

close(data_sock);

/* 客户端接收服务器的响应码和信息,正常为 ”226 Transfer complete.” */

read(control_sock, read_buf, read_len);

 

/* 命令 ”QUIT\r\n” */

sprintf(send_buf,"QUIT\r\n");

/* 客户端将断开与服务器端的连接 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码,正常为 ”200 Closes connection.” */

read(control_sock, read_buf, read_len);

/* 客户端关闭控制连接 */

close(control_sock);

至此,下载文件已经完成。需要注意的是发送 FTP 命令的时候,在命令后要紧跟 “\r\n”,否则服务器不会返回信息。回车换行符号 “\r\n” 是 FTP 命令的结尾符号,当服务器接收到这个符号时,认为客户端发送的命令已经结束,开始处理。否则会继续等待。

让我们来看一下 FTP 服务器这一端的响应情况:

清单 6. 客户端下载文件时,FTP 服务器的响应输出

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

(not logged in) (127.0.0.1)> Connected, sending welcome message...

(not logged in) (127.0.0.1)> 220-FileZilla Server version 0.9.36 beta

(not logged in) (127.0.0.1)> 220 hello gaoleyi

(not logged in) (127.0.0.1)> USER gaoleyi

(not logged in) (127.0.0.1)> 331 Password required for gaoleyi

(not logged in) (127.0.0.1)> PASS *********

gaoleyi (127.0.0.1)> 230 Logged on

gaoleyi (127.0.0.1)> PWD

gaoleyi (127.0.0.1)> 257 "/" is current directory.

gaoleyi (127.0.0.1)> SIZE file.txt

gaoleyi (127.0.0.1)> 213 4096

gaoleyi (127.0.0.1)> PASV

gaoleyi (127.0.0.1)> 227 Entering Passive Mode (127,0,0,1,13,67)

gaoleyi (127.0.0.1)> RETR file.txt

gaoleyi (127.0.0.1)> 150 Connection accepted

gaoleyi (127.0.0.1)> 226 Transfer OK

gaoleyi (127.0.0.1)> QUIT

gaoleyi (127.0.0.1)> 221 Goodbye

首先,服务器准备就绪后返回 220。客户端接收到服务器端返回的响应码后,相继发送“USER username” 和 “PASS password” 命令登录。随后,服务器返回的响应码为 230 开头,说明客户端已经登入了。这时,客户端发送 PASV 命令让服务器进入被动模式。服务器返回如 “227 Entering Passive Mode (127,0,0,1,13,67)”,客户端从中得到端口号,然后连接到服务器的数据端口。接下来,客户端发送下载命令,服务器会返回响应码 150,并从数据端口发送数据。最后,服务器返回 “226 transfer complete”,表明数据传输完成。

需要注意的是,客户端不要一次发送多条命令,例如我们要打开一个目录并且显示这个目录,我们得发送 CWD dirname,PASV,LIST。在发送完 CWD dirname 之后等待响应代码,然后再发送后面一条。当 PASV 返回之后,我们打开另一个 Socket 连接到相关端口上。然后发送 LIST,返回 125 之后在开始接收数据,最后返回 226 表明完成。

在传输多个文件的过程中,需要注意的是每次新的传输都必须重新使用 PASV 获取新的端口号,接收完数据后应该关闭该数据连接,这样服务器才会返回一个 2XX 成功的响应。然后客户端可以继续下一个文件的传输。

上传文件与下载文件相比,登入验证和切换被动模式都如出一辙,只需要改变发送到服务器端的命令,并通过数据连接发送文件内容。

客户端通过被动模式向服务器上传文件

当客户端发送命令上传文件,服务器会从数据连接接收文件。

图 6. 客户端连接到 FTP 服务器的数据端口并上传文件

客户端连接到 FTP 服务器的数据端口并上传文件

客户端通过主动模式向服务器上传文件

到目前为止,本文介绍的都是客户端用被动模式进行文件的上传和下载。下面将介绍客户端用主动模式下载文件。

图 7. 用主动模式从 FTP 服务器下载文件

用主动模式从 FTP 服务器下载文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

清单 7. 用主动模式从 FTP 服务器下载文件的示例 C 程序

... ...

SOCKET data_sock;

data_sock = socket(AF_INET, SOCK_STREAM, 0);

struct  sockaddr_in  name;

name.sin_family = AF_INET;

name.sin_addr.s_addr = htons(INADDR_ANY);

server_port = p1*256+p2;

length = sizeof(name);

name.sin_port = htons(server_port);

bind(server_sock, (struct sockaddr *)&name, length);

struct  sockaddr_in client_name;

length = sizeof(client_name);

 

/* 客户端开始监听端口p1*256+p2 */

listen(server_sock, 64);

 

/* 命令 ”PORT \r\n” */

sprintf(send_buf,"PORT 1287,0,0,1,%d,%d\r\n", p1, p2);

write(control_sock, send_buf,strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”200 Port command successful” */

read(control_sock, read_buf, read_len);

 

sprintf(send_buf,"RETR filename.txt\r\n");

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”150 Opening data channel for file transfer.” */

read(control_sock, read_buf, read_len);

 

/* ftp客户端接受服务器端的连接请求 */

data_sock = accept(server_sock,(struct sockaddr *)&client_name, &length);

... ...

 

file_handle = open(disk_name, ROFLAGS, RWXALL);

for( ; ; ) {

... ...

read(data_sock, read_buf, read_len);

write(file_handle, read_buf, read_len);

... ...

}

close(file_handle);

客户端通过 PORT 命令告诉服务器连接自己的 p1*256+p2 端口。随后在这个端口进行监听,等待 FTP 服务器连接上来, 再通过这个数据端口来传输文件。PORT 方式在传送数据时,FTP 客户端其实就相当于一个服务器端,由 FTP 服务器主动连接自己。

断点续传

由于网络不稳定,在传输文件的过程中,可能会发生连接断开的情况,这时候需要客户端支持断点续传的功能,下次能够从上次终止的地方开始接着传送。需要使用命令 REST。如果在断开连接前,一个文件已经传输了 512 个字节。则断点续传开始的位置为 512,服务器会跳过传输文件的前 512 字节。

清单 8. 从 FTP 服务器断点续传下载文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

... ...

/* 命令 ”REST offset\r\n” */

sprintf(send_buf,"REST %ld\r\n", offset);

/* 客户端发送命令指定下载文件的偏移量 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,

*正常为 ”350 Restarting at <position>. Send STORE or RETRIEVE to initiate transfer.” */

read(control_sock, read_buf, read_len);

... ...

 

/* 命令 ”RETR filename\r\n” */

sprintf(send_buf,"RETR %s\r\n",filename);

/* 客户端发送命令从服务器端下载文件, 并且跳过该文件的前offset字节*/

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,*

*正常为 ”150 Connection accepted, restarting at offset <position>” */

read(control_sock, read_buf, read_len);

... ...

 

file_handle = open(disk_name, CRFLAGS, RWXALL);

/* 指向文件写入的初始位置 */

lseek(file_handle, offset, SEEK_SET);

... ...

结束语

本文从应用实现的角度,介绍了 FTP 协议。并用详尽的例子分析了如何用主动模式和被动模式实现 FTP 客户端上传下载文件,如何进行断点续传。通过本文可以让读者对 FTP 客户端的原理有一个深入的了解。

这里借用轻飘飞扬博主的代码测试通过

#include<stdio.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<netinet/in.h>
#include<netdb.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SERV_PORT 21
#define MAXSIZE 1024
#define SA struct sockaddr
static int control_sockfd;
int npsupport;
int login_yes;
int f;//f=0时为默认文件结构
int login();
void ftp_list(int control_sockfd);
void zeromery(char *a,int len);
void ftp_pwd(int control_sockfd);
void ftp_changdir(char dir[],int control_sockfd);
void ftp_quit(int control_sockfd);
void ftp_creat_mkd(char *path,int control_sockfd);
void ftp_back(int control_sockfd);
void ftp_stru(int control_sockfd);
void ftp_rest(int control_sockfd);
int ftp_download(int control_sockfd);
char *itoa(int value, char *string, int radix);
int  main(int argc,char **argv)
{
    printf("ftp>");
    char command[MAXSIZE];
    char*cmd;
    scanf("%s",command);
    cmd=command;
    while(*(cmd)==' ')
        cmd++;
 
    if(strncmp(cmd,"login",5)==0)
    {
        login();
        if(login_yes==1)
        {
            while(1)
            {
            comm:            
                sleep(1);
                printf("ftp>");
                zeromery(command,1024);
                scanf("%s",command);
                cmd=command;
                while(*(cmd)==' ')
                cmd++;
                if(strncmp(cmd,"pasv",4)==0)
                {
                    ftp_list(control_sockfd);
                }
                if(strncmp(cmd,"port",4)==0)
                {
                    ftp_list(control_sockfd);
                }
                if(strncmp(cmd,"list",4)==0)
                {
                    ftp_pwd(control_sockfd);
                    ftp_list(control_sockfd);
                }
                if(strncmp(cmd,"pwd",3)==0)
                {
                    ftp_pwd(control_sockfd);
                }
                if(strncmp(cmd,"mkdir",5)==0)
                {
                    char path[60];
                    zeromery(path,60);
                    printf("创建的路径名: ");
                    scanf("%s",path);
                    printf("s/n",path);
                    ftp_creat_mkd(path,control_sockfd);
                }
                if(strncmp(cmd,"back",4)==0)
                {
                    ftp_back(control_sockfd);
 
                    ftp_pwd(control_sockfd);
                }
                if(strncmp(cmd,"cd",2)==0)
                {
                    int i;
                    char path[60];
                    zeromery(path,60);
                    printf("要到的路径:");
                    scanf("%s",path);
                    printf("%s/n",path);
                    ftp_changdir(path,control_sockfd);
                }
                if(strncmp(cmd,"get",3)==0)
                {
                    ftp_pwd(control_sockfd);    
                    ftp_download(control_sockfd);
                }
                if(strncmp(cmd,"up",3)==0)
                {
                    ftp_pwd(control_sockfd);
                    ftp_up(control_sockfd);        
                }
                if(strncmp(cmd,"quit",4)==0)
                {
                    printf("bye^_^/n");
                    close(control_sockfd);
                    break;
                }
                printf("支持 list,pwd,mkdir,back,cd,up,get/n");    
        }
        
    }
    else if(login_yes==0)
    { 
        int i;//不成功登录下最多还有两次机会,如果不能在两次登录,则,关闭链接。
        printf("Can not login vsftpd");
        for(i=2;i>0;i--)
        {
            printf("你还有 %d 登录机会/n",i);            
            login();
            if(login_yes==1)
            {
                goto comm;
            }
        }
        if(i==0)
        {
            printf("你不能在登录!/n");
            close(control_sockfd);
        }
            
    }
    else if (strncmp(cmd,"quit",4)==0)
    {
        ftp_quit(control_sockfd);
        close(control_sockfd);
        
    }
    }
    return 0;
}
int login()
{
    //初始化端口信息
    struct sockaddr_in serv_addr;
    char senddate,recvdate;
    char sendline[MAXSIZE],recvline[MAXSIZE];
    struct hostent *host;
 
    //获取hostent中相关参数
    char name[MAXSIZE],password[MAXSIZE];
    printf("please enter the hostname/n");
    printf("ftp-> ");
    scanf("%s",name);
    host=gethostbyname(name);
    if(host==NULL)
    {
        printf("get host by name is error!/n");
        login_yes=0;
    }
    else
    {
        //创建socket
        control_sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(control_sockfd<0)
        {
            printf("socket is error/n");
            login_yes=0;
        }
    
 
        //设置sockaddr_in 结构体中的相关参数
        bzero(&serv_addr,sizeof(serv_addr));
        serv_addr.sin_family=AF_INET;
        serv_addr.sin_port=htons(SERV_PORT);
        serv_addr.sin_addr.s_addr=INADDR_ANY;
 
        //调用connect函数发起连接
        char addr[MAXSIZE];
        if((connect(control_sockfd,(SA*)&serv_addr,sizeof(serv_addr)))<0)
        {
            printf("connect is error/n");
            login_yes=0;
        }
        printf("connect to %s/n",inet_ntop(AF_INET,host->h_addr,addr,1024));
        recvdate=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(recvdate==-1)
        {
            printf("recvdate is connect error/n");
            login_yes=0;
        }
        else if(strncmp(recvline,"220",3)==0)
        {
            printf("connect success,pelase enter username/n");
            login_yes=1;
        }
        else 
        {
            printf("220 connect is error!");
            login_yes=0;    
        }
 
        //ftp用户登录主体部分
        int sendbytes,recvbytes;
        zeromery(name,1024);
        zeromery(password,1024);
        zeromery(recvline,1024);
        zeromery(sendline,1024);
        printf("ftp-> ");
        scanf("%s",name);//可以支持匿名登录vsftpd
        strcat(sendline,"USER ");
        strcat(sendline,name);
        strcat(sendline,"/r/n");
        printf("--->%s/n",sendline);
        sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
        if(sendbytes==-1)
        {
            printf("send is wrong/n");
            login_yes=0;
        }
        recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(strncmp(recvline,"331",3)==0)
        {
            printf("331 please specify the password./n");
        }
        else
        {
            printf("recv date is error./n");
            login_yes=0;
        }
        zeromery(sendline,1024);
        zeromery(recvline,1024);
        printf("ftp-> ");
        scanf("%s",password);
        strcat(sendline,"PASS ");
        strcat(sendline,password);
        strcat(sendline,"/r/n");
        printf("--->%s/n",sendline);
        sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
        if(sendbytes==-1)
        {
            printf("pass send is error/n");
            login_yes=0;
        }
        recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(strncmp(recvline,"230",3)==0)
        {
            printf("login success!/n");
            login_yes=1;
        }
        else 
        {
            printf("pass recv is error/n");
            login_yes=0;
        }
 
 
        //支持断点续传
        zeromery(sendline,1024);
        zeromery(recvline,1024);
        strcat(sendline,"REST ");
        strcat(sendline,"0");
        strcat(sendline,"/r/n");
        printf("--->%s/n",sendline);
        sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
        if(sendbytes==-1)
        {
            printf("rest send is error!/n");
            login_yes=0;
        }
        recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(recvbytes==-1)
        {
            printf("rest recv date is error./n");    
            login_yes=0;    
        }
        if(strncmp(recvline,"350 Restart position accepted (0).",34)==0)
        {
            npsupport=1;
            printf("support 断点续传/n");
            login_yes=1;
        }
        else
        {
            npsupport=0;
            printf("not support 断点续传/n");
            login_yes=0;    
        }
    
 
        //获取服务器版本信息
        zeromery(recvline,1024);
        zeromery(sendline,1024);
        strcat(sendline,"SYST");
        strcat(sendline,"/r/n");
        printf("--->%s/n",sendline);
        sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
        if(sendbytes==-1)
        {
            printf("syst send is error/n");
            login_yes=0;
        }
        recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(recvbytes==-1)
        {
            printf("syst recv is error/n");
            login_yes=0;
        }
        if(strncmp(recvline,"215 UNIX Type: L8",17)==0)
        {
            printf("%s",recvline);
            login_yes=1;
        }
        else 
        {
            printf("syst recv connectin is error/n");
            login_yes=0;
        }    
    }
    
    return login_yes;
}
 
//数组初始化
void zeromery(char *a,int len)
{
    int i;
    len=sizeof(a);
    for(i=0;i<len;i++)
    {
        a[i]=0;
    }
}
 
//quit函数,control_sockfd,通过实参传递
void ftp_quit(int control_sockfd )
{
    char sendline[1024];
    char recvline[1024];
    int recvbytes;
    int sendbytes;
    zeromery(sendline,1024);
    zeromery(recvline,1024);
    strcat(sendline,"QUIT");
    strcat(sendline,"/r/n");
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
        printf("quit send is error!/n");
        exit(1);
    }
    recvbytes=recv(control_sockfd,recvline,strlen(recvline),0);
    if(strncmp(recvline,"221",3)==0)
    {
        printf("221 bye!^_^");
        exit(1);
    }
    else
    {
        printf("quit recv is error!/n");
        exit(1);
    }
}
 
 
//mkd,在所在路径中创建目录 函数
void ftp_creat_mkd(char *path,int control_sockfd)
{
    char sendline[1024];
    char recvline[1024];
    zeromery(sendline,1024);
    zeromery(recvline,1024);
    int recvbytes,sendbytes;
    int issuccess;
    strcat(sendline,"MKD ");
    strcat(sendline,path);
    strcat(sendline,"/r/n");
    printf("%s/n",sendline);
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
        printf("mkd send is error!");
        exit(1);
    }
    recvbytes=recv(control_sockfd,recvline,strlen(recvline),0);
    if(strncmp(recvline,"257",3)==0)
    {
        issuccess=1;
    }
    else
    {
        issuccess=0;
    }
}
 
 
//改变目录函数chdir
void  ftp_changdir(char *dir,int control_sockfd)
{
    
    char sendline[1024];
    char recvline[1024];
    int recvbytes,sendbytes;
    zeromery(sendline,1024);
    zeromery(recvline,1024);
    strcat(sendline,"CWD ");
    strcat(sendline,dir);
    strcat(sendline,"/r/n");
    printf("%s/n",sendline);
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
        printf("cwd send is error!/n");
    }
    recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
    if(recvbytes<0)
    {
        printf("cwd recv is error!/n");
    }
    if(strncmp(recvline,"250",3)==0)
    {
                char buf[55];
                snprintf(buf,39,">>> %s/n",recvline);
                printf("%s/n",buf);
    }
    else
    {
        printf("cwd chdir is error!/n");
        exit(1);
    }
}
 
 
//pwd 命令函数
//在应答中返回当前工作目录,“pwd”+/r/n
void ftp_pwd(int control_sockfd)
{
    int recvbytes,sendbytes;
    char sendline[1024],recvline[1024];
    zeromery(sendline,1024);
    zeromery(recvline,1024);
    strcat(sendline,"PWD");
    strcat(sendline,"/r/n");
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
        printf("pwd,send is error/n");
    }
    recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
    if(strncmp(recvline,"257",3)==0)
    {
        int i=0;
        char *ptr;
        char currendir[1024];
        zeromery(currendir,1024);
        ptr=recvline+5;
        while(*(ptr)!='"')
        {
            currendir[i++]=*(ptr);
            ptr++;
        }
        currendir[i]='/0';
        printf("current directory is:%s/n",currendir);
 
    }
    else
    {
        printf("pwd,recv is error!/n");
    }
}
 
 
 
//获取服务器文件列表
//list命令,是数据通道,通过的是21端口。the function 's struct is "the data //transport mode"(ascii or b) puls "the data mode to transport"(pasv or port//) puls "the list command"
void ftp_list(int control_sockfd)
{
    int pasv_or_port;// 定义the ftp协议的两种不同工作mode
    int recvbytes,sendbytes;
    char sendline[1024],recvline[1024];
    struct sockaddr_in serv_addr;
    int i,j;
    int flag=0;
    int data_sockfd;
 
    //用户来选择pasv 或者是 port mode(默认的是pasv模式)
    char selectdata_mode_tran[1024];
    zeromery(selectdata_mode_tran,1024);
    zeromery(sendline,1024);
    zeromery(recvline,1024);
    //printf("ftp->ftp协议工作方式选择(pasv or port)/n");
    //printf("ftp->");
//    scanf("%s",selectdata_mode_tran);
    //if(strncmp(selectdata_mode_tran,"pasv",4)==0)
    //{
        pasv_or_port=0;
//    }
//    if(strncmp(selectdata_mode_tran,"port",4)==0)
//    {
//        pasv_or_port=1;
//    }
    //pasv mode
    if(pasv_or_port==0)
    {
        strcat(sendline,"PASV");
        strcat(sendline,"/r/n");
        sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
        if(sendbytes<0)
        {
            printf("pasv send is error!/n");
        }
        recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(recvbytes<0)
        {
            printf("pasv recv is error!/n");
        }
        if(strncmp(recvline,"227",3)==0)
        {
            printf("%s/n",recvline);
        }
        else
        {
            printf("pasv recv is error!/n");
        }    
        //处理ftp server 端口
        char *ptr1,*ptr2;
        char num[1024];
        zeromery(num,1024);
        //取低位字节
        ptr1=recvline+strlen(recvline);
        while(*(ptr1)!=')')
        {
            ptr1--;
        }
        ptr2=ptr1;
        while(*(ptr2)!=',')
            ptr2--;
        strncpy(num,ptr2+1,ptr1-ptr2-1);
        i=atoi(num);//将字符串转换成整数
        //取高位字节
        zeromery(num,1024);
        ptr1=ptr2;
        ptr2--;
        while(*(ptr2)!=',')
            ptr2--;
        strncpy(num,ptr2+1,ptr1-ptr2-1);
        j=atoi(num);
        //初始化服务器数据连接时的端口信息
        int data_serviceport;
        data_serviceport=j*256+i;
        data_sockfd=socket(AF_INET,SOCK_STREAM,0);
                
        serv_addr.sin_family=AF_INET;
        serv_addr.sin_addr.s_addr=INADDR_ANY;
        serv_addr.sin_port=htons(data_serviceport);
        if(connect(data_sockfd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr))==-1)
        {
            printf("pasv data connect is error!/n");
        }
    }
    //port mode
    if(pasv_or_port==1)
    {
        data_sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(data_sockfd<0)
        {
            printf("创建数据端口连接失败!/n");
        }
        serv_addr.sin_family=AF_INET;
        serv_addr.sin_addr.s_addr=INADDR_ANY;
        serv_addr.sin_port=htons(SERV_PORT);
        
        int ret;
        int addrlen;
        ret=sizeof(struct sockaddr_in);
        getsockname(data_sockfd,(SA*)&serv_addr,&ret);
        
        //处理port 后面要带的参数
        char ip[1024];
        int i,j;
        char data[1024];
        zeromery(ip,1024);
        zeromery(data,1024);
        inet_ntop(AF_INET,&(serv_addr.sin_addr),ip,sizeof(ip));
        printf("%s/n",ip);
        i=data_sockfd/256;
        j=data_sockfd%256;
 
        //将点分十进制的点转换为逗号。
        char *ptr1;
        ptr1=ip;
        while(*(ptr1)!='/0')
        {
            if(*(ptr1)=='.')
            {
                *(ptr1)=',';
            }
            ptr1++;
        }
 
        strcat(sendline,"PORT ");
        strcat(sendline,ip);
        strcat(sendline,",");
        strcat(sendline,itoa(i,data,10));
        strcat(sendline,",");
        strcat(sendline,itoa(j,data,10));
        strcat(sendline,"/r/n");
        printf("--->%s/n",sendline);
        sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
        if(sendbytes<0)
        {
            printf("port send is error!/n");
            exit(1);
        }
        recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(strncmp(recvline,"200",3)==0)
        {
            printf("%s/n",recvline);
        }
        else
        {
            printf("port recv is error!/n");
        }
 
    }
 
    //type
    zeromery(recvline,1024);
    zeromery(sendline,1024);
    strcat(sendline,"TYPE ");
    strcat(sendline,"I");
    strcat(sendline,"/r/n");
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
        printf(" type send is error!/n");
    }
    recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
    if(strncmp(recvline,"200",3)==0)
    {
        printf("使用二进制传输数据/n");
    }
    else
    {
        printf("type recv is error!/n");
    }
        
 
    //list
    zeromery(sendline,1024);
    zeromery(recvline,1024);
    strcat(sendline,"LIST");
    strcat(sendline,"/r/n");
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
        printf("list send is error!/n");
    }
recvdata:
    sleep(1);
    recvbytes=recv(data_sockfd,recvline,sizeof(recvline),0);
    if(recvbytes<0)
    {
        close(data_sockfd);
        goto ending;
    }
    printf("%s",recvline);
    if(flag==0)
    {
        zeromery(recvline,1024);
        recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(strncmp(recvline,"226",3)!=0)
        {
            flag=1;
            goto recvdata;
        }
    }
ending:
    if(flag!=1)
    {
        zeromery(recvline,1024);
    }
    close(data_sockfd);    
}
 
 
//itoa 函数的实现(整数转换成字符串)
char *itoa(int value, char *string, int radix)
{
    char tmp[33];
    char *tp = tmp;
    int i;
    unsigned v;
    int sign;
    char *sp;
 
    sign = (radix == 10 && value < 0);
    if (sign)
        v = -value;
    else
        v = (unsigned)value;
    while (v || tp == tmp)
    {
        i = v % radix;
        v = v / radix;
        if (i < 10)
          *tp++ = i+'0';
        else
          *tp++ = i + 'a' - 10;
    }
 
    if (string == 0)
        string = (char *)malloc((tp-tmp)+sign+1);
    sp = string;
 
    if (sign)
        *sp++ = '-';
    while (tp > tmp)
        *sp++ = *--tp;
    *sp = 0;
    return string;
}
 
 
//back 返回上一级函数,相当于cd /;
void ftp_back(int control_sockfd)
{
    char sendline[1024],recvline[1024];
    int recvbytes,sendbytes;
    zeromery(sendline,1024);
    zeromery(recvline,1024);
    strcat(sendline,"CDUP");
    strcat(sendline,"/r/n");
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
        printf("cdup send is error !/n");
    }
    recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
    if(recvbytes<0)
    {
        printf("cdup recv is error !/n");
    }
    if(strncmp(recvline,"250",3)==0)
    {
        printf("请求的文件操作已经成功/n");
    }
}
//stru命令的实现
void ftp_stru(int control_sockfd)
{
    int recvbytes,sendbytes;
    char sendline[1024],recvline[1024];
    zeromery(sendline,1024);
    zeromery(recvline,1024);
    strcat(sendline,"STRU");
    strcat(sendline,"F");
    strcat(sendline,"/r/n");
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
        printf("stru send is error!/n");
    }
    recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
    if(recvbytes<0)
    {
        printf("stru recv is error!/n");
    }
    if(strncmp(recvline,"200",3)==0)
    {
        f=0;
    }
 
}
 
//断点函数的支持
void ftp_rest(int control_sockfd)
{
 
    int recvbytes,sendbytes;
    char sendline[1024],recvline[1024];
    zeromery(sendline,1024);
    zeromery(recvline,1024);
    strcat(sendline,"REST ");
    strcat(sendline,"500");
    strcat(sendline,"/r/n");
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
        printf("stru send is error!/n");
    }
    recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
    if(recvbytes<0)
    {
        printf("stru recv is error!/n");
    }
    if(strncmp(recvline,"350",3)==0)
    {
        printf("%s/n",recvline);
    }
}
 
//下载的实现函数
int ftp_download(int control_sockfd)
{
    
    int pasv_or_port;// 定义the ftp协议的两种不同工作mode
    int recvbytes,sendbytes;
    char sendline[1024],recvline[1024];
    struct sockaddr_in serv_addr;
    FILE *fd;
    int i,j;
    int data_sockfd;
    //rest
    ftp_rest(control_sockfd);
    //type
    zeromery(recvline,1024);
    zeromery(sendline,1024);
    strcat(sendline,"TYPE ");
    strcat(sendline,"I");
    strcat(sendline,"/r/n");
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
            printf(" type send is error!/n");
    }
    recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
    if(strncmp(recvline,"200",3)==0)
    {
        printf("使用二进制传输数据/n");
    }
    else
    {
        printf("type recv is error!/n");
    }
 
    if(npsupport==1)
    {
        //open the file
        int size;
        char localpathname[60];//预打开的文件路径字符串
        int flags;
        char pathname[60];
        unsigned int mode;
        //用户来选择pasv 或者是 port mode
        char selectdata_mode_tran[1024];
        zeromery(selectdata_mode_tran,1024);
        zeromery(sendline,1024);
        zeromery(recvline,1024);
        pasv_or_port=0;//(默认是pasv模式)
        //pasv mode
        if(pasv_or_port==0)
        {
            strcat(sendline,"PASV");
            strcat(sendline,"/r/n");
            sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
            if(sendbytes<0)
            {
                printf("pasv send is error!/n");
            }
            zeromery(recvline,1024);
            recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
            if(recvbytes<0)
            {
                printf("pasv recv is error!/n");
            }
            if(strncmp(recvline,"227",3)==0)
            {
                char buf[55];
                snprintf(buf,51,">>> %s/n",recvline);
                printf("%s/n",buf);
            }
            else
            {
                printf("pasv recv is error!/n");
            }    
            //处理ftp server 端口
            char *ptr1,*ptr2;
            char num[1024];
            zeromery(num,1024);
            //取低位字节
            ptr1=recvline+strlen(recvline);
            while(*(ptr1)!=')')
            {
                ptr1--;
            }
            ptr2=ptr1;
            while(*(ptr2)!=',')
                ptr2--;
            strncpy(num,ptr2+1,ptr1-ptr2-1);
            i=atoi(num);//将字符串转换成整数
            //取高位字节
            zeromery(num,1024);
            ptr1=ptr2;
            ptr2--;
            while(*(ptr2)!=',')
                ptr2--;
            strncpy(num,ptr2+1,ptr1-ptr2-1);
            j=atoi(num);
            //初始化服务器数据连接时的端口信息
            int data_serviceport;
            data_serviceport=j*256+i;
            data_sockfd=socket(AF_INET,SOCK_STREAM,0);
                
            serv_addr.sin_family=AF_INET;
            serv_addr.sin_addr.s_addr=INADDR_ANY;
            serv_addr.sin_port=htons(data_serviceport);
            if(connect(data_sockfd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr))==-1)
            {
                printf("pasv data connect is error!/n");
            }
            printf("remote-file-pathname=");
            scanf("%s",pathname);
            printf("local-file-pathname=");
            scanf("%s",localpathname);
            printf("local:%s remore:%s/n",localpathname,pathname);
            fd=fopen(localpathname,"w+");
            if(fd==NULL)
            {
                printf("cannot open file/n");
                exit(1);
            }
 
    
            //send the command retr;
            zeromery(sendline,1024);
            zeromery(recvline,1024);
            strcat(sendline,"RETR ");
            strcat(sendline,pathname);
            strcat(sendline,"/r/n");
            printf("%s/n",sendline);
            sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
            if(sendbytes<0)
            {
                printf("retr send is error!/n");
            }
            recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
            if(recvbytes<0)
            {
                printf("retr recv is error!/n");
            }
            if(strncmp(recvline,"400",3)>0)
            {
                printf("return is error!/n");
            }
        }
        //port mode
        /*if(pasv_or_port==1)
        {
            data_sockfd=socket(AF_INET,SOCK_STREAM,0);
            if(data_sockfd<0)
            {
                printf("创建数据端口连接失败!/n");
            }
            serv_addr.sin_family=AF_INET;
            serv_addr.sin_addr.s_addr=INADDR_ANY;
            serv_addr.sin_port=htons(SERV_PORT);
        
            int ret;
            int addrlen;
            ret=sizeof(struct sockaddr_in);
            getsockname(data_sockfd,(SA*)&serv_addr,&ret);
        
            //处理port 后面要带的参数
            char ip[1024];
            int i,j;
            char data[1024];
            zeromery(ip,1024);
            zeromery(data,1024);
            inet_ntop(AF_INET,&(serv_addr.sin_addr),ip,sizeof(ip));
            printf("%s/n",ip);
            i=data_sockfd/256;
            j=data_sockfd%256;
            //将点分十进制的点转换为逗号。
            char *ptr1;
            ptr1=ip;
            while(*(ptr1)!='/0')
            {
                if(*(ptr1)=='.')
                {
                    *(ptr1)=',';
                }
                ptr1++;
            }
            strcat(sendline,"PORT ");
            strcat(sendline,ip);
            strcat(sendline,",");
            strcat(sendline,itoa(i,data,10));
            strcat(sendline,",");
            strcat(sendline,itoa(j,data,10));
            strcat(sendline,"/r/n");
            printf("--->%s/n",sendline);
            sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
            if(sendbytes<0)
            {
                printf("port send is error!/n");
                exit(1);
            }
            recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
            if(strncmp(recvline,"200",3)==0)
            {
                printf("%s/n",recv);
            }
            else
            {
                printf("port recv is error!/n");
            }
        }*/
        //begin to transpotr data
        sleep(1);
        int flag=0;
        char buffer[65536];
recvdata:
        zeromery(buffer,1024);
        recvbytes=recv(data_sockfd,buffer,sizeof(buffer),0);
        if(recvbytes<0)
        {
            close(data_sockfd);
            goto end;
        }
        fwrite(buffer,1,recvbytes,fd);
        zeromery(recvline,1024);
        recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(flag==0)
        {
            if(strncmp(recvline,"226",3)!=0)
            {
                    flag=1;
                    goto recvdata;
            }
        }
end:
        if(flag!=1)
        {
            zeromery(recvline,1024);
            shutdown(data_sockfd,SHUT_WR);
            close(data_sockfd);
        }
        close(data_sockfd);
/*    int err;
    char buffer[65535];
    err=read(data_sockfd,buffer,sizeof(buffer));
    sleep(5);
    fwrite(buffer,1,err,fd);
    sleep(5);
    zeromery(recvline,1024);
    recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
    if(strncmp(recvline,"226",3)==0)
    {
    end:    printf("226 transfer complete/n");
        close(data_sockfd);
    }*/
    return 0;
    }
}
 
 
//up 函数
int ftp_up(int control_sockfd)
{
        
    int pasv_or_port;// 定义the ftp协议的两种不同工作mode
    int recvbytes,sendbytes;
    char sendline[1024],recvline[1024];
    struct sockaddr_in serv_addr;
    FILE *fd;
    int i,j;
    int data_sockfd;
    //type
    zeromery(recvline,1024);
    zeromery(sendline,1024);
    strcat(sendline,"TYPE ");
    strcat(sendline,"I");
    strcat(sendline,"/r/n");
    sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
    if(sendbytes<0)
    {
            printf(" type send is error!/n");
    }
    recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
    if(strncmp(recvline,"200",3)==0)
    {
        printf("使用二进制传输数据/n");
    }
    else
    {
        printf("type recv is error!/n");
    }
 
    if(npsupport==1)
    {
        //open the file
        int size;
        char localpathname[60];//预打开的文件路径字符串
        int flags;
        char pathname[60];
        unsigned int mode;
        //用户来选择pasv 或者是 port mode
        char selectdata_mode_tran[1024];
        zeromery(selectdata_mode_tran,1024);
        zeromery(sendline,1024);
        zeromery(recvline,1024);
        pasv_or_port=0;//(默认是pasv模式)
        //pasv mode
        if(pasv_or_port==0)
        {
            strcat(sendline,"PASV");
            strcat(sendline,"/r/n");
            sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
            if(sendbytes<0)
            {
                printf("pasv send is error!/n");
            }
            recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
            if(recvbytes<0)
            {
                printf("pasv recv is error!/n");
            }
            if(strncmp(recvline,"227",3)==0)
            {
                char buf[55];
                snprintf(buf,51,">>> %s/n",recvline);
                printf("%s/n",buf);
            }
            else
            {
                printf("pasv recv is error!/n");
            }    
            //处理ftp server 端口
            char *ptr1,*ptr2;
            char num[1024];
            zeromery(num,1024);
            //取低位字节
            ptr1=recvline+strlen(recvline);
            while(*(ptr1)!=')')
            {
                ptr1--;
            }
            ptr2=ptr1;
            while(*(ptr2)!=',')
                ptr2--;
            strncpy(num,ptr2+1,ptr1-ptr2-1);
            i=atoi(num);//将字符串转换成整数
            //取高位字节
            zeromery(num,1024);
            ptr1=ptr2;
            ptr2--;
            while(*(ptr2)!=',')
                ptr2--;
            strncpy(num,ptr2+1,ptr1-ptr2-1);
            j=atoi(num);
            //初始化服务器数据连接时的端口信息
            int data_serviceport;
            data_serviceport=j*256+i;
            data_sockfd=socket(AF_INET,SOCK_STREAM,0);
                
            serv_addr.sin_family=AF_INET;
            serv_addr.sin_addr.s_addr=INADDR_ANY;
            serv_addr.sin_port=htons(data_serviceport);
            if(connect(data_sockfd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr))==-1)
            {
                printf("pasv data connect is error!/n");
            }
                printf("local-file-pathname=");
                scanf("%s",pathname);
                printf("remote-file-pathname=");
                scanf("%s",localpathname);
                printf("local:%s remore:%s/n",localpathname,pathname);
                fd=fopen(pathname,"r");
                if(fd==NULL)
                {
                    printf("cannot open file,请重新输入!/n");
                }
 
            //send the command retr;
            zeromery(sendline,1024);
            zeromery(recvline,1024);
            strcat(sendline,"STOR ");
            strcat(sendline,localpathname);
            strcat(sendline,"/r/n");
            printf("%s/n",sendline);
            sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
            if(sendbytes<0)
            {
                printf("stor send is error!/n");
            }
            recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
            if(recvbytes<0)
            {
                printf("retr recv is error!/n");
            }
            if(strncmp(recvline,"150",3)==0)
            {
                char buf[55];
                snprintf(buf,25,">>> %s/n",recvline);
                printf("%s/n",buf);
            }
        }
        //port mode
        /*if(pasv_or_port==1)
        {
            data_sockfd=socket(AF_INET,SOCK_STREAM,0);
            if(data_sockfd<0)
            {
                printf("创建数据端口连接失败!/n");
            }
            serv_addr.sin_family=AF_INET;
            serv_addr.sin_addr.s_addr=INADDR_ANY;
            serv_addr.sin_port=htons(SERV_PORT);
        
            int ret;
            int addrlen;
            ret=sizeof(struct sockaddr_in);
            getsockname(data_sockfd,(SA*)&serv_addr,&ret);
        
            //处理port 后面要带的参数
            char ip[1024];
            int i,j;
            char data[1024];
            zeromery(ip,1024);
            zeromery(data,1024);
            inet_ntop(AF_INET,&(serv_addr.sin_addr),ip,sizeof(ip));
            printf("%s/n",ip);
            i=data_sockfd/256;
            j=data_sockfd%256;
            //将点分十进制的点转换为逗号。
            char *ptr1;
            ptr1=ip;
            while(*(ptr1)!='/0')
            {
                if(*(ptr1)=='.')
                {
                    *(ptr1)=',';
                }
                ptr1++;
            }
            strcat(sendline,"PORT ");
            strcat(sendline,ip);
            strcat(sendline,",");
            strcat(sendline,itoa(i,data,10));
            strcat(sendline,",");
            strcat(sendline,itoa(j,data,10));
            strcat(sendline,"/r/n");
            printf("--->%s/n",sendline);
            sendbytes=send(control_sockfd,sendline,strlen(sendline),0);
            if(sendbytes<0)
            {
                printf("port send is error!/n");
                exit(1);
            }
            recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
            if(strncmp(recvline,"200",3)==0)
            {
                printf("%s/n",recv);
            }
            else
            {
                printf("port recv is error!/n");
            }
        }*/
        //begin to transpotr data
    while(!feof(fd))
    {        
        char buffer[65536];
        zeromery(buffer,sizeof(buffer));
        int size;
        size=fread(buffer,1,sizeof(buffer),fd);
        if(ferror(fd))
        {
            printf("read file data is error!/n");
            break;
        }
        else
        {
            zeromery(sendline,1024);
            sendbytes=send(data_sockfd,buffer,size,0);
            printf("传输了 %d 个字节/n",sendbytes);
        }
        close(data_sockfd);
        recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);
        if(strncmp(recvline,"226",3)==0)
        {
            printf("226 transfer complete");
            break;
        }
    }
    return 0;
    }
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FTP 客户端C实现 的相关文章

  • 串口通信中接收数据时延迟处理与缓存处理的解决方案(C#)

    利用串口进行通信 xff0c 当发送方 xff08 A xff09 将数据写入串口后 xff0c 通过无线或有线方式将数据传送给接收方 xff08 B xff09 xff0c B通过调用串口读方法comm read 参数 即可将数据读出 原
  • robomaster电控究极学习教程(以哨兵为例)------一、串口dma和遥控器

    robomaster电控究极学习教程 xff08 以哨兵为例 xff09 一 串口dma和遥控器 文章目录 robomaster电控究极学习教程 xff08 以哨兵为例 xff09 一 串口dma和遥控器 一 串口DMA的作用二 步骤1 c
  • 无人机架构

    无人机架构 基本设计需求 xff0c 如 xff1a xff08 1 xff09 慎思规划和反应式行为 xff1b xff08 2 xff09 容许不确定性 xff1b xff08 3 xff09 考虑危险 xff1b xff08 4 xf
  • 第八课 C++中的__LINE__宏

    在C 43 43 编程中 xff0c 我们有时候需要知道当前源代码的行号 xff0c 这时候就可以使用 LINE 宏 本文将介绍如何使用 LINE 宏 xff0c 以及它的使用示例 LINE 宏简介 LINE 是C 43 43 中的一个预定
  • 介绍@dynamic的用法

    介绍 64 dynamic的用法 Objective C 2 0提供了属性 64 property xff0c 可以让编译器自动生成setter和getter方法 如果不想编译器自作主张生成这些setter和getter方法 xff0c 则
  • OkHttpUtils的使用

    OkHttpUtils是一个非常好的网络协议框架 xff0c 它是在OkHttp的基础上进行了二次封装 要使用这个类首先下载jar包 xff0c 如下 xff1a http download csdn net download xxdw19
  • 十进制整数转为十六进制整数(C++实现)

    一 代码功能 xff1a 输入一个十进制整数 xff0c 将其转化为十六进制整数并输出 二 源码 include lt iostream gt include lt cstring gt include lt cmath gt using
  • 我的四轴专用PID参数整定方法及原理---超长文慎入(转)

    给四轴调了好久的PID xff0c 总算是调好了 xff0c 现分享PID参数整定的心得给大家 xff0c 还请大家喷的时候手下留情 首先说明一下 xff0c 这篇文章的主旨并不是直接教你怎么调 xff0c 而是告诉你这么调有什么道理 xf
  • 简单的TCP客户端发包工具

    一 TCP介绍 先放这里有时间在写 xff0c 最近在写DuiLib相关的使用内容 xff0c 这部分大家凑活着看 二 程序截图 下载链接 链接 xff1a https pan baidu com s 1MzNUzwd7WwBat6vNMc
  • C++常用头文件汇总

    之前说过的头文件这就来了 1 首先是本人最喜欢也是最最方便的万能头文件 xff0c 顾名思义 xff0c 不管是天上飘的还是地下埋的 xff0c 只要不是不对的头文件它都包含 xff0c 除了本篇第14个头文件 xff1a include
  • 基于I2C/SPI的温湿度采集与OLED显示

    基于I2C SPI的温湿度采集与OLED显示 一 AHT20温湿度采集1 I2C2 温湿度采集代码效果 二 OLED显示1 显示学号姓名2 诗句显示 三 总结四 参考 一 AHT20温湿度采集 1 I2C 解释什么是 软件I2C 和 硬件I
  • 游戏客户端编程

    游戏客户端编程 1 代码实现服务器连接发送按钮 2 显示消息3 发送信息4 播放背景音乐5 变换游戏背景图片6 参考 1 代码实现 服务器连接 span class token keyword private span span class
  • 卷积神经网络实现表情识别

    卷积神经网络实现表情识别 CNN人脸表情识别图片预处理原本效果处理后效果 图片数据集效果 CNN人脸识别创建模型归一化与数据增强创建网络 摄像头人脸识别图片识别 参考 CNN人脸表情识别 图片预处理 span class token key
  • IIC协议及其工程【FPGA】

    IIC协议及其工程 FPGA 一 IIC协议1 IIC简介2 IIC中相关的术语1 应答信号 xff08 ACK xff09 2 无应答信号 xff08 NOACK xff09 3 虚写3 IIC的时序 二 EEPROM1 时钟频率2 起始
  • char *与char []的区别

    其实 xff0c 只要记住一点就能很好区分char 和char xff1a char 定义的是一个指向字符串的指针 xff08 注意 xff1a C语言中没有对应字符串的内置类型或者类类型 xff09 xff0c 而char 就是C语言中的
  • SPI协议读取FLASH【FPGA】

    SPI协议读取FLASH FPGA 一 SPI协议1 SPI简介2 SPI物理层3 SPI协议层CPOL CPHA 及通讯模式 4 SPI 基本通讯过程5 通讯的起始和停止信号6 数据有效性 二 Flash1 状态寄存器1 WIP xff0
  • 基于FPGA的图像边缘检测

    基于FPGA的图像边缘检测 一 图像处理算法1 灰度转换2 高斯滤波3 二值化4 Sobel 二 项目框架1 摄像头配置模块2 图像处理模块3 数据缓存模块4 其它模块 三 部分代码1 数据采集模块2 读写控制模块 四 参考五 源码 简介
  • 基本认证_摘要认证_HTTPS

    1 HTTP基本认证 HTTP提供了一个原生的质询 响应 xff08 challenge response xff09 框架 Web应用程序收到一条HTTP请求报文时 xff0c 服务器没有按照请求执行动作 xff0c 而是以一个 认证质询
  • 什么是奇校验(Odd Parity),什么是偶校验(Even Parity)?

  • 2021-03-08

    今天在网上安装PR xff0c 网上下载的安装器把电脑默认装了一大堆垃圾工具 xff0c 依次删除后突然发现谷歌浏览器主页被篡改了 xff0c 随后用360等工具修复 xff0c 均提示无异常 通过浏览器设置和重置主页后仍然无效 xff0c

随机推荐

  • 2021-03-08

    大疆无人机自己动手更换电芯的注意事项 xff0c 当电池多电芯出现均大压差且调整数据无效后 xff0c 或发现某块或多块电芯鼓包 xff0c 说明电芯已经老化 xff0c 寿命用尽 xff0c 就需要更换电芯了 xff0c 厂家为保护消费者
  • can't run '/etc/init.d/rcS': No such file or directory 最终解决方法

    drivers rtc hctosys c unable to open rtc device rtc0 end request I O error dev mtdblock2 sector 256 isofs fill super bre
  • Ubuntu下的CuteCom串口详细调试教程

    I MX6ULL嵌入式开发学习 串口调试 一 Ubuntu下的串口调试助手安装 嵌入式开发学习过程中学习到串口调试这一章 xff0c 以前在Win10操作时都有相对应的串口调试界面 xff0c 安装个串口驱动在电脑设备端口里面看到COM3时
  • STM32 串口

    文章目录 USART 通信协议RS 232与TTL电平 串口通信数据包组成USART功能框图讲解引脚寄存器状态寄存器 USART SR 数据寄存器 USART DR 控制寄存器 USART CR 波特比率寄存器 USART BRR 发送过程
  • printf二进制数据

    基于之前这篇文章的代码改进了下 xff1a http blog csdn net xzongyuan article details 28889063 之前打印的数字没有补0 我打印了内存信息 xff0c 结果是这样的 xff0c 不能对齐
  • 分析MySQL数据类型的长度

    分析MySQL数据类型的长度 MySQL有几种数据类型可以限制类型的 34 长度 34 xff0c 有CHAR Length VARCHAR Length TINYINT Length SMALLINT Length MEDIUMINT L
  • 为vscode配置clangd

    目录 安装clangd 后端安装clangd 前端修改基础配置生成compile commands json文件基本效果补全warning提醒自动修改存在问题 注意事项 clangd能提供更好的补全和提示 xff0c 自带检查一些warni
  • 论文笔记(十九)RGB-D Object Tracking: A Particle Filter Approach on GPU

    RGB D Object Tracking A Particle Filter Approach on GPU 文章概括摘要1 介绍2 贡献3 粒子滤波器4 可能性评估5 实施细节6 实验A 物体模型B 合成序列C 真实序列 7 结论8 鸣
  • Ubuntu 命令行 访问网页

    安装w3m 1 进入 root apt get install w3m 2 测试是否成功 xff1a w3m https blog csdn net x xx xxx xxxx article details 92574331
  • 代码管理中Trunk、Branches、Tags的区别和联系

    我们可以将这三者想象成一棵树的组成部分 trunk为树干branches为树枝tags为整棵树 trunk用于主线开发 branches用于定制版本 修复bugs 并行开发等使用 tags用于存放release版本 xff0c 阶段性代码
  • linux使用curl请求(带参数)

    1 2 3 curl G d 34 c 61 amp a 61 34 http www net index php
  • 惯导系列(二):滤波相关的算法

    前言 我又消失了一段时间 xff0c 这段时间研究了惯性导航有关的算法 xff0c 整理了不少博客 xff0c 字数比较多 xff0c 图片比较多 学到了很多知识 目录 前言 本节介绍 一 Mahony算法 1 1 PID控制算法 1 2
  • STM32 CAN 设置多个过滤器接收多ID方法

    1 标识符列表模式 xff0c 32位模式下 void MX CAN Init void 这里是实现了两个地址的接收 一个是用来接收广播信息 一个用来接收私有地址 如果想实现多个地址可以添加多个过滤器组 stm32103 有0 13 共14
  • linux下运行动态库问题 cannot open shared object file: No such file or directory

    如果动态库不在同一级目录下 xff0c 则需要将以上文件的目录加载到动态库搜索路径中 xff0c 设置的方式有以下几种 一 将动态库路径加入到LD LIBRARY PATH环境变量 1 在终端输入 xff1a export LD LIBRA
  • 几个串口通信协议的整理

    一 UART UART是一个大家族 xff0c 其包括了RS232 RS499 RS423 RS422和RS485等接口标准规范和总线标准规范 它们的主要区别在于其各自的电平范围不相同 嵌入式设备中常常使用到的是TTL TTL转RS232的
  • 单片机中断的过程

    1 根据响应的中断源的中断优先级 使相应的优先级状态触发器置1 xff1b 2 把当前程序计数器PC的内容压入堆栈 xff0c 保护断点 xff0c 寻找中断源 xff1b 3 执行硬件中断服务子程序调用 xff1b 4 清除相应的中断请求
  • Ruby学习札记(3)- Ruby中gem的安装与卸载

    Ruby 学习札记 3 Ruby 中 gem 的安装与卸载 在 Ruby 中有 gem 包这种概念 xff0c 类似 PHP 中的 pear xff0c 相当于一种插件 具体可以 Google 一下 xff08 1 xff09 查看已经安装
  • 【linux】ubuntu20.04 运行软件 提示找不到过时的库 libQtCore.so.4、libQtGui.so.4、libpng12.so.0

    先上结果 1 nxView运行起来 环境 硬件 xff1a Jetson Xavier NX 套件 系统 xff1a Ubuntu 20 04 软件 xff1a nxView 43 libQtCore so 4 解决 0 现象 运行软件提示
  • rtt相关问题总结

    1 总结RT Thread的启动流程 xff08 启动文件部分跳过 xff09 关中断 rt hw interrupt disable 板级初始化 xff1a 需在该函数内部进行系统堆的初始化 rt hw board init 打印 RT
  • FTP 客户端C实现

    使用 Socket 通信实现 FTP 客户端程序 FTP 概述 文件传输协议 xff08 FTP xff09 作为网络共享文件的传输协议 xff0c 在网络应用软件中具有广泛的应用 FTP的目标是提高文件的共享性和可靠高效地传送数据 在传输