socket和ftp,ftp客户端和服务端传递ftp命令时

  socket和ftp,ftp客户端和服务端传递ftp命令时

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

  利用Socket通信实现FTP客户端程序软件工程师高是IBM中国系统与技术中心的软件工程师,从事测试工具的开发工作。顾,现任IBM中国系统与技术中心软件工程师,从事测试工具的开发工作。软件工程师顾是IBM中国系统与技术中心的软件工程师,从事测试工具的开发工作。简介:FlashFXP、File Zilla等FTP客户端应用广泛。原则上都是由底层套接字实现的。对于FTP客户端和服务器之间的数据交换,必须建立两个套接字,一个作为命令通道,另一个作为数据通道。前者用于客户端向服务器发送命令,如登录和删除文件,后者用于接收数据,如下载或上传文件。详细阐述了如何调用系统接口发送FTP命令来实现文件上传、下载等FTP客户端功能,使读者对FTP客户端的原理有一个深入的了解。

  本文标签:ftp,linux网络编程,网络,套接字,使用,客户端程序,教学,通信实现,通用编程

  标记这篇文章!发布日期:2011年4月7日

  级别:初级

  浏览次数:22620次

  评论:0(查看添加评论-登录)

  平均分(94分)

  给这篇文章打分。

  FTP概述

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

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

  回到顶端

  文件传送协议

  与其他协议(如HTTP)相比,FTP更复杂。与一般C/S应用不同的是,一般C/S应用通常只建立一个Socket连接,同时处理服务器和客户端的连接命令和数据传输。FTP协议中命令和数据分开传输的方法提高了效率。

  FTP使用两个端口,一个数据端口和一个命令端口(也称为控制端口)。这两个端口一般是21(命令端口)和20(数据端口)。控制套接字用于传输命令,数据套接字用于传输数据。在每个FTP命令被发送后,FTP服务器将返回一个字符串,包括一个响应代码和一些解释信息。返回码主要用于判断命令是否被成功执行。

  命令端口

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

  数据端口

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

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

  如果使用主动模式,通常客户端会向服务器发送一个端口号,它会在这个端口监视器中。服务器需要连接客户端打开的数据端口,传输数据。

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

  活动模式(端口)

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

  FTP客户端只是告诉服务器它自己的端口号,并让服务器连接到客户端指定的端口。对于客户端的防火墙来说,这是由外向内的连接,可能会被屏蔽。

  被动模式(PASV)

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

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

  第一个端口连接到服务器的端口21,并提交PASV命令。然后,服务器会打开任意端口(P 1024),返回“227进入被动模式(127,0,0,1,4,18)”。它返回227开头的信息。括号中有六个用逗号分隔的数字。前四个是服务器的地址,后两个是服务器的地址。倒数第二个乘以256,加上最后一个数字。这是FTP服务器为传输数据而开放的端口。如果获得227进入被动模式(H1、H2、H3、H4、P1、P2),则端口号为p1*256 p2,ip地址为h1.h2.h3.h4,这意味着服务器上有一个开放的端口。客户端收到获取端口号的命令后,会通过端口N 1连接到服务器的端口P,然后在两个端口之间传输数据。

  使用的主要FTP命令

  每个FTP命令由3到4个字母组成,后跟参数,用空格分隔。每个命令都以 rn 结尾。

  要下载或上传文件,首先登录FTP服务器,然后发送命令,最后退出。在这个过程中,使用的主要命令是用户、通过、大小、休息、CWD、RETR、PASV、港口和退出。

  用户:指定用户名。它通常是控制连接后发出的第一个命令。"用户gaoleyirn ":用户名是gaoleyi登录名。

  通过:指定用户密码。该命令紧跟在用户命令之后。" PASS gaoleyirn ":密码是gaoleyi。

  SIZE:从服务器返回指定文件的大小。" SIZE file.txtrn ":如果file.txt存在,则返回文件的大小。

  CWD:改变工作目录。例如“CWD dirnamern”。

  PASV:让服务器在数据端口监视器中进入被动模式。例如“PASVrn”。

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

  RETR:下载文件。" RETR文件. txt rn ":下载文件file.txt

  STOR:上传文件。" STOR文件. txtrn ":上传文件file.txt

  REST:这个命令不传输文件,但是跳过指定点之后的数据。该命令后面应该跟有其他需要文件传输的FTP命令。" REST 100rn ":将文件传输的偏移量重新指定为100字节。

  退出:关闭与服务器的连接。

  FTP响应代码

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

  响应代码由三位数代码表示:

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

  第二个数字是响应类型的分类,例如2表示与连接相关的响应,3表示用户身份验证。

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

  第一个数字的含义如下:

  1表示服务器已正确接收到信息,但尚未进行处理。

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

  3表示服务器已正确接收到信息并正在处理它。

  4表示信息暂时错误。

  5表示信息永远是错误的。

  第二个数字的含义如下:

  0表示语法。

  1表示系统状态和信息。

  2表示连接状态。

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

  4表示未定义。

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

  套接字编程的几个重要步骤

  套接字编程的主要步骤如下:

  Socket()创建一个Socket connect()来连接服务器write()和read()进行会话Close()Close Socket服务器端编程。主要步骤如下:

  Socket()创建一个套接字bind() listen()来侦听accept()以接收连接请求write()和read()来关闭会话()以关闭套接字返回到页面顶部。

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

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

  在客户端和FTP服务器之间建立套接字连接。发送用户并向服务器传递命令以登录FTP服务器。使用PASV命令获取服务器监控的端口号并建立数据连接。使用RETR/STOR命令下载/上传文件。下载后,断开数据连接,发送退出命令退出。本例中使用的FTP服务器是filezilla。在整个交互过程中,控制连接始终处于连接状态,每次传输一个文件,数据连接先打开后关闭。

  在客户端和FTP服务器之间建立套接字连接。

  当客户端与服务器连接时,服务器将返回一个响应代码220和一些欢迎信息。

  图一。客户端连接到服务器

  清单1。客户端连接到FTP服务器并接收欢迎消息。

  SOCKET control _ sock

  struct hostent * hp

  struct sockaddr_in服务器;

  memset( server,0,sizeof(struct sockaddr _ in));

  /*初始化套接字*/

  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。然后,客户端可以向服务器发送命令。

  图二。客户端登录到FTP服务器

  2.客户端发送用户名和密码并登录到FTP服务器。

  /* command "用户用户名 r n" */

  sprintf(send_buf,用户%srn ,用户名);

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

  write(control_sock,send_buf,strlen(send _ buf));

  /*客户端收到服务器的响应代码和信息,通常是“331用户名没问题,需要密码。”*/

  read(control_sock,read_buf,read _ len);

  /*命令" pass password r n" */

  sprintf(send_buf, PASS %srn ,password);

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

  write(control_sock,send_buf,strlen(send _ buf));

  /*客户端接收服务器的响应代码和信息,通常是“230用户登录,已处理”*/

  read(control_sock,read_buf,read _ len);

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

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

  图3。客户端将服务器置于被动模式。

  清单3。让服务器在数据端口监视器中进入被动模式

  /* command "pasv r n" */

  sprintf(send_buf, PASV r n );

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

  write(control_sock,send_buf,strlen(send _ buf));

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

  *正常是“227进入被动模式(H1、H2、H3、H4、P1、P2)”*/

  read(control_sock,read_buf,read _ len);

  客户端以被动模式下载文件。

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

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

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

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

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

  /*命令" cwd目录名 r n" */

  sprintf(send_buf, CWD %srn ,dirname);

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

  write(control_sock,send_buf,strlen(send _ buf));

  /*客户端接收服务器的响应代码和信息,通常是“250 command okay”*/

  read(control_sock,read_buf,read _ len);

  /* command "size文件名 r n" */

  sprintf(send_buf, SIZE %srn ,filename);

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

  write(control_sock,send_buf,strlen(send _ buf));

  /*客户端接收服务器的响应代码和信息,通常是“213大小”*/

  read(control_sock,read_buf,read _ len);

  /* command "retrfilename r n" */

  sprintf(send_buf, RETR %srn ,文件名);

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

  write(control_sock,send_buf,strlen(send _ buf));

  /*客户端接收服务器的响应码和信息,一般是“150打开数据连接。”*/

  read(control_sock,read_buf,read _ len);

  /*客户端创建文件*/

  file_handle=open(disk_name,CRFLAGS,rwx all);

  for(;) {

  .

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

  read(data_sock,read_buf,read _ len);

  /*客户端编写文件*/

  write(file_handle,read_buf,read _ len);

  .

  /*客户端关闭文件*/

  RC=close(file _ handle);

  客户端退出服务器。

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

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

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

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

  close(data _ sock);

  /*客户端接收服务器的响应代码和信息,通常为“226传输完成”*/

  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 closesconnection”*/

  read(control_sock,read_buf,read _ len);

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

  close(control _ sock);

  至此,文件下载已经完成。需要注意的是,发送FTP命令时,命令后面应该跟“rn”,否则服务器不会返回信息。回车符“rn”是FTP命令的结束符号。当服务器收到此符号时,它认为客户端发送的命令已经结束并开始处理。否则,我们将继续等待。

  我们来看看FTP服务器这一端的响应:

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

  (未登录)(127.0.0.1)已连接,正在发送欢迎消息.

  (未登录)(127.0.0.1) 220-FileZilla服务器版本0.9.36测试版

  (未登录)(127.0.0.1) 220你好高乐易

  (未登录)(127.0.0.1)用户高乐易

  (未登录)(127.0.0.1)高乐易需要331密码

  (未登录)(127.0.0.1)通过********

  高勒伊(127.0.0.1) 230登陆

  高勒伊(127.0.0.1) PWD

  gaoleyi (127.0.0.1) 257 /是当前目录。

  高乐易(127.0.0.1)大小file.txt

  高乐意(127.0.0.1) 213 4096

  高勒伊(127.0.0.1) PASV

  高勒伊(127.0.0.1) 227进入被动模式(127,0,0,1,13,67)

  高乐易(127.0.0.1) RETR file.txt

  gaoleyi (127.0.0.1) 150连接被接受

  高乐意(127.0.0.1) 226转账OK

  高乐义(127.0.0.1)退出

  高乐意(127.0.0.1) 221再见

  首先,服务器在准备就绪时返回220。客户端收到服务器的响应码后,发送“用户用户名”和“密码”命令登录。然后,服务器返回的响应代码以230开头,表示客户端已经登录。此时,客户端发送PASV命令,使服务器进入被动模式。服务器返回如“227进入被动模式(127,0,0,1,13,67)”,客户端从中获取端口号,然后连接到服务器的数据端口。接下来,客户端发送下载命令,服务器返回响应代码150,并从数据端口发送数据。最后服务器返回“226传输完成”,表示数据传输完成。

  请注意,客户端不应该一次发送多个命令。例如,如果我们想打开一个目录并显示它,我们必须发送CWD迪纳尔PASV的列表。发送CWD目录名后等待响应代码,然后发送以下代码。当PASV返回时,我们打开另一个套接字并连接到相关端口。然后发送列表,返回125,开始接收数据,最后返回226表示完成。

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

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

  客户端以被动模式上传文件到服务器。

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

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

  客户端在主动模式下将文件上传到服务器。

  到目前为止,本文描述了客户端使用被动模式上传和下载文件。下面描述了客户端在主动模式下下载文件。

  图7。在活动模式下从FTP服务器下载文件

  清单7。示例C程序,用于在活动模式下从FTP服务器下载文件

  .

  SOCKET data _ sock

  data_sock=socket(AF_INET,SOCK_STREAM,0);

  struct sockaddr_in名称;

  name.sin _ family=AF _ INET

  name . sin _ addr . s _ addr=htons(in addr _ ANY);

  server _ port=p1 * 256 p2

  length=sizeof(name);

  name . sin _ port=htons(server _ port);

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

  client_name中的struct sockaddr _ in

  length=sizeof(client _ name);

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

  listen(server_sock,64);

  /* command "port r n" */

  sprintf(send_buf,端口1287,0,0,1,%d,%drn ,p1,p2);

  write(control_sock,send_buf,strlen(send _ buf));

  /*客户端接收服务器的响应代码和信息,通常为“200端口命令成功”*/

  read(control_sock,read_buf,read _ len);

  sprintf(send_buf, RETR文件名. txt r n );

  write(control_sock,send_buf,strlen(send _ buf));

  /*客户端接收到服务器的响应码和信息,一般是“150打开数据通道进行文件传输。”*/

  read(control_sock,read_buf,read _ len);

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

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

  .

  file_handle=open(disk_name,ROFLAGS,rwx all);

  for(;) {

  .

  read(data_sock,read_buf,read _ len);

  write(file_handle,read_buf,read _ len);

  .

  close(file _ handle);

  通过客户端端口命令告诉服务器连接其p1*256 p2端口。然后在这个端口监听,等待FTP服务器连接,然后通过这个数据端口传输文件。端口模式传输数据时,FTP客户端实际上相当于一个服务器,FTP服务器主动连接自己。

  超文本传送协议(Hyper Text Transport Protocol的缩写)

  由于网络的不稳定性,在传输文件的过程中,连接可能会断开。这时候客户端需要支持断点续传的功能,下次就可以从上次终止的地方开始传文件了。需要命令REST。如果文件在断开连接之前已经传输了512个字节。那么断点续传的起始位置是512,服务器会跳过传输文件的前512个字节。

  清单8。从FTP服务器断点继续下载文件

  .

  /*命令" rest offset r n" */

  sprintf(send_buf, REST %ldrn ,offset);

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

  write(control_sock,send_buf,strlen(send _ buf));

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

  *正常是“350恢复原位。发送store或retrieve以启动传输。*/

  read(control_sock,read_buf,read _ len);

  .

  /* command "retrfilename r n" */

  sprintf(send_buf, RETR %srn ,文件名);

  /*客户端发送命令从服务器下载文件,并跳过文件的第一个偏移字节*/

  write(control_sock,send_buf,strlen(send _ buf));

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

  *正常是“接受150连接,在偏移位置恢复”*/

  read(control_sock,read_buf,read _ len);

  .

  file_handle=open(disk_name,CRFLAGS,rwx all);

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

  lseek(file_handle,offset,SEEK _ SET);

  .

  回到顶端

  结束语

  本文从应用实现的角度介绍了FTP协议。结合具体实例,分析了如何利用主动模式和被动模式实现FTP客户端上传下载文件,以及如何在断点处继续传输。通过本文,读者可以对FTP客户端的原理有一个深入的了解。

socket和ftp,ftp客户端和服务端传递ftp命令时