ftp客户端代码

被动模式

1.被动模式通讯介绍

首先,服务器准备就绪后返回 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 成功的响应。然后客户端可以继续下一个文件的传输。

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

2. 客户端建立连接,设置服务器为被动模式

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);
//返回220
* 命令 ”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);
/* 命令 ”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);
/* 命令 ”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);

3.数据通道代码

/* 连接服务器新开的数据端口 */
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);

4.关闭客户端通道

/* 客户端关闭数据连接 */
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);

5.服务器相应输出

(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

主动模式


1.连接和传输

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);

2.断点续传

... ...
/* 命令 ”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);
... ...

http://www.ibm.com/developerworks/cn/linux/l-cn-socketftp/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值