C语言实现FTP客户端(已编译,亲试可用~)

C语言实现被动模式下,FTP客户端从FTP服务器下载文件

  • 1、搭建FTP服务器

    • 此处用到的FTP服务器是在Win10系统下搭建的,具体服务器搭建与配置过程可参照另一篇我写的文章《Win10搭建FTP服务器详细教程》,本文中用到的FTP命令里的传递参数举例皆与上面提到的FTP服务器配置匹配。

  • 2、名词解释

    • 1、FTP协议

      • FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一。FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。
    • 2、被动模式(Passive)

      • FTP服务器收到PASV命令后,随机打开一个高端端口P(端口号大于1024)并且通知客户端在这个端口上传送数据的请求,客户端连接FTP服务器此端口,通过三次握手建立通道,然后FTP服务器将通过这个端口进行数据的传送。
    • 3、命令通道(Command Socket)

      • 客户端打开一个本地端口N(N > 1024)用来连接FTP服务器的 21 端口,建立命令通道,它负责 FTP 命令的发送和接收服务器返回的响应信息。
    • 4、数据通道(Data Socket)

      • 对于有数据传输的操作,比如传输下载文件,我们需要建立另一个数据通道来完成。进入被动模式后,服务器会打开一个端口P(P > 1024),此时客户端会打开一个新的端口(N + 1)去连接服务器的端口P,此通道负责传输文件数据。

  • 3、主要用到的FTP命令和对应的FTP响应码

    • 1、FTP命令

       FTP 每个命令都有 3 到 4 个字母组成,命令后面跟参数,用空格分开。每个命令都以 "\r\n"结束。
      • USER:“USER xiaokeai\r\n”    → 用户名为:xiaokeai
      • PASS:“PASS 123456\r\n”      → 密码为:123456
      • TYPE:“TYPE I\r\n”         → 设置传输方式为二进制模式:I
      • SIZE:“SIZE /AC1/APP.bin\r\n”      → 获取APP.bin这文件的大小,文件路径:/AC1/APP.bin
      • PASV:"PASV\r\n "         → 让服务器进入被动模式
      • RETR:“RETR /AC1/APP.bin \r\n”   → 下载命令,文件路径:/AC1/APP.bin
      • QUIT:“QUIT\r\n”           → 退出FTP服务器命令
    • 2、FTP响应码

       客户端发送 FTP 命令后,服务器返回响应码,响应码用三位数字编码表示。
      • 220:“220 Microsoft FTP Service”             → 命令通道已连接,服务器发送欢迎信息
      • 331:”331 Password required”                → 用户名正确,请求密码
      • 230:”230 User logged in.”                   → 用户已登录
      • 200:”200 Tpye set to I.”                 → 文件传输模式已改为二进制模式
      • 213:”213 7968”                     → APP.bin文件大小为:7968个字节
      • 227:”227 Entering Passive Mode (192,168,1,110,19,190).”  → 服务器IP:192.168.1.110
                                     服务器数据端口号:19 * 256 + 190 = 5054
      • 125:”125 Data connection already open; Transfer starting.” → 数据连接已建立,开始传输数据
      • 226:”226 Transfer complete.”              → 数据传输完毕,提示客户端断开数据连接
      • 221:”221 Transfer complete.”              → 提示客户端断开命令连接

  • 4、Socket 编程的几个重要步骤

    • socket( );   → 服务器/客户端打开一个端口
    • connect( );   → 创建一个命令/数据通道连接
    • listen( );   → 监听
    • close( );   → 断开命令/数据通道连接

  • 5、实现被动模式下,FTP客户端从FTP服务器下载文件

    • 1、客户端和服务器建立命令通道连接

      • 客户端端口N和服务器端口21建立命令通道连接后,服务器返回“220…”欢迎信息
        C语言实现FTP客户端(已编译,亲试可用~)_第1张图片
    • 2、客户端登陆FTP服务器

      • 客户端发送用户名和密码,服务器验证通过后,会返回230的响应码。然后客户端就可以向服务器端发送命令了。
        C语言实现FTP客户端(已编译,亲试可用~)_第2张图片
    • 3、客户端设置文件传输方式,获取文件信息

      • 客户端发送TYPE命令设置文件传输方式为二进制,再发送SIZE命令获取待下载文件的大小(单位字节)
        C语言实现FTP客户端(已编译,亲试可用~)_第3张图片
    • 4、客户端让FTP服务器进入被动模式,建立数据通道连接

      • 客户端在下载文件前,要先发送PASV命令让服务器进入被动模式。进入被动模式后,服务器会打开数据端口P > 1024并监听,并返回响应码 227,227响应信息里包含服务器IP和端口号P的信息,客户端打开端口N + 1,与服务器端口P建立数据通道连接。
        C语言实现FTP客户端(已编译,亲试可用~)_第4张图片
    • 5、客户端通过被动模式下载文件

      • 建立数据通道连接后,客户端向服务器发送RETR命令下载文件,服务器会返回125响应码,并开始通过数据通道向客户端发送文件内容。
        C语言实现FTP客户端(已编译,亲试可用~)_第5张图片
    • 6、下载完成,客户端退出服务器

      • 当客户端下载完成后,服务器会发226响应码,客户端断开数据通道连接,然后服务器会发221响应码,客户端再断开命令通道连接。
        C语言实现FTP客户端(已编译,亲试可用~)_第6张图片
  • 6、部分代码展示

    • 213响应解析函数,获取下载文件大小

      //举例:”213 7968”
      uint8 Get_213_FileSize(char *arg)
      {
      	uint16 n;
      	char* tok = 0;
          tok = strtok(arg, " ");
      	tok = strtok(NULL, "\r\n");
      	
      	File_Size = atoi32(tok, 10);
      	printf(" File_Size is : %d byte\n", File_Size);
      	return 1;
      }
      
    • 227响应解析函数,获取服务器 IP 和端口号 P

      //227 Entering passive mode (h1,h2,h3,h4,p1,p2)
      uint8 Get_227_IpPort(char *arg)
      {
      	int i;
      	int j = 0;
        	char* tok = 0;
        	strtok(arg, "(");														//去掉括号以前的字符串,留下h1,h2,h3,h4,p1,p2)
      	
      	//获取 IP
        	for(i = 0; i < 4; i++)
          {
      		if(i == 0) 
      		{
      			tok = strtok(NULL, ",\r\n");									//取 h1
      		}
      		else   
      		{
      			tok = strtok(NULL, ",");										//分别取 h2, h3, h4
      		}	
      		ftp_data_ip[i] = (uint8_t)atoi16(tok, 10);
      		
      		if(!tok)
      	    {
      			printf(" bad ipport : %s\r\n", arg);
      		    return 0;
      	    }
          }
      	
      	//获取端口号 port
      	j = 0;
      	ftp_data_port = 0;
      	for (i = 0; i < 2; i++)
      	{
      		if(j == 0)
      		{
      			tok = strtok(NULL, ",");
      			ftp_data_port = (atoi16(tok, 10) << 8);
      			j++;
      		}
      		else   
      		{
      			tok = strtok(NULL, ",");
      			tok = strtok(tok, ")");
      			ftp_data_port += (uint16_t)(atoi16(tok, 10) & 0xff);
      		} 
      		
      	    if(!tok)
      		{
      			 printf(" bad ipport : %s\r\n", arg);
      			 return 0;
      	    }
      	}
        	printf(" ip : %d.%d.%d.%d, port : %d\r\n", ftp_data_ip[0], ftp_data_ip[1], ftp_data_ip[2], ftp_data_ip[3], ftp_data_port);
        	return 1;
      }
      
    • 服务器响应数据的接收函数

       void proc_ftpconrol(char * buf)
      {
      	uint16_t Responses;
      	uint8_t dat[30]={0,};
      	memset(dat,0,30);
      	Responses = (buf[0] - '0') * 100 + (buf[1] - '0') * 10 + (buf[2] - '0');
      	printf(" Responses : %d\r\n", Responses);
      	switch(Responses)
      	{
      		case 220:																//连上服务器之后,接收到服务器发送的220 欢迎文字
      			printf("\r\n Input your User ID > ");
      			sprintf((char*)dat, "USER %s\r\n", "xiaokeai");							
        			printf("\r\n");
      			send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat));		//用USER命令发送用户名
      			break;
      	
      		case 331:																//服务器接收到用户名,返回331状态
      			printf("\r\n Input your Password > ");
        			sprintf((char*)dat, "PASS %s\r\n", "123456");
       			printf("\r\n");
      			send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat));		//用PASS命令发送登录密码
        			break;
      	
      		case 230:																//服务器接收到密码后,返回230表示密码正确,返回530表示密码错误
        			printf("\r\n User logged in, proceed > ");
        			sprintf((char*)dat, "TYPE %c\r\n", TransferBinary);
      			printf("\r\n");
      			send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat));		//登陆成功后,再发送一条TYPE I命令,进入二进制模式,这样获取的文件数据的时候,就会以二进制字节流发送,避免以ASCII格式发送文件数据
        			break;
      	
      		case 200:																//服务器返回200
      			//如果服务器还没进入被动模式,此时表示设置二进制模式成功
      			printf("\n Get File Size...");
      			sprintf((char*)dat, "SIZE %s\r\n", "/AC1/APP.bin");							
      			printf("\r\n");
      			send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat));		//发送SIZE命令,获取下载文件的大小
        			break;
      		
      		case 213:																//接收SIZE命令,服务器返回 213 FileSize  (文件大小单位为字节,10进制数)
      			Get_213_FileSize(buf);
      			printf("\r\n Set Server To Passive Mode...");
      			sprintf((char*)dat, "PASV\r\n");
      			printf("\r\n");
      			send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat));	 	//发送PASV命令,设置服务器进入被动模式
        			break;
      		
      		case 227:																//接收PPASV命令,服务器返回 227 Entering passive mode (h1,h2,h3,h4,p1,p2)
        			if(Get_227_IpPort(buf) == 0)
      			{
          			printf(" Bad port syntax\r\n");
        			}
        			else
      			{
          			printf(" Go Set Up Data Sock...\r\n ");							//服务器已进入被动模式,准备建立 Data Socket 数据传输通道 
      			}
        			break;
      		
      		case 125:																//建立数据通道连接后,服务器返回“125 Data connection already open Transfer starting”
      			printf(" Entering Download Mode...\r\n\n");
      			FlashAddrCur = FLASH_APP2_ADDR;
      			RecDataLenCur = 0;
      			break;
      	
      		case 226:																//服务器发送完升级包后,主动发送226,服务器那边断开数据连接通道了
      			close(SOCK_FTP_DATA);												//客户端关闭数据传输socket
      			printf(" Client Close Data Socket...\r\n");
      	
      			printf("\r\n Quit of the Server...\r\n");
      			sprintf((char*)dat, "QUIT\r\n");
      			send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat));		//发送QUIT命令,请求退出服务器
      			break;
      	
      		case 221:																//服务器接收到QUIT命令,返回的221
      			printf(" Client Close Control Socket...\r\n");
      			close(SOCK_FTP_CTRL);												//客户端关闭命令传输socket
      			break;
      		
      		default:
            		printf("\r\n Default Status = %d\r\n",(uint16_t)Responses);
           	 	break;
      	}
      }
      
  • 7、小结

  欢迎纠正,(づ ̄3 ̄)づ╭❤~
  如果有帮助到你,能点个赞吗?ღ( ´・ᴗ・` ) 比心

你可能感兴趣的:(FTP,ftp,服务器,c语言)