W5500EVB作为FTP客户端


关键词:FTP Client  W5500  WIZnet  W5500EVB  FTP客户端


W5500 EVB使用主动模式与网络中的FTP服务器进行指令和数据的交互,所以,必须创建两个TCP的socket。一个TCP请求用于会话通道创建,另一个用于数据通道创建。遵循下面的程序来建立FTP客户端。

  1. 创建一个TCP服务器监听一个特定的端口。该服务器将被用做数据通道(来自FTP服务器的入口数据连接)。
  2. 执行一个TCP客户机请求,该TCP客户机请求通过端口21到任何FTP服务器。这将用作会话通道。
  3. 在成功创建连接后,给出需要的登录认证信息(使用标准的FTP命令)来连接FTP服务器。服务器将回复登录成功的提示。注意:考虑到内存大小和常用命令,该应用笔记只支持ls,cd,put,get和bye。其他命令不支持。
  4. 根据FTP标准,如果必须执行任何相关数据的操作,则需要在一个新的数据通道中执行(来自FTP服务器的入口数据)。因此实现/创建该数据连接,FTP客户端需要提供详细的IP地址和端口号,该端口将接收来自FTP服务器的入口数据。为了达到这些要求,FTP协议建议使用命令“PORT”。如果FTP服务器针对发出的端口命令返回一个成功消息,连接已准备好数据通信了。
  5. 执行任何数据相关的FTP命令时,FTP服务器初始化一个入口数据通道到TCP服务器,该TCP 服务器在W5500中处于监听模式下。FTP服务器使用端口命令提供的信息来初始化该数据连接。
  6. 在数据传输结束后,FTP服务器将关闭该数据通道。
  7. 为了以后相关数据的活动,通过发出另一个端口命令重新启用数据通道,再启用会话通道。

完成以上流程,有两个主要的函数:ftp_client_PI()和ftp_client_DTP(),第一个函数负责指令传输,第二个负责数据传输。

下面我们看一下程序上的实现
	void ftp_client_PI (uint8 sock)
	{
		int   len;
		int   idx;
		char  msg[STR_SIZE];
	    uint16 status;
		static uint16 any_port =50000;
	    memset(cmd_buf,0,sizeof(cmd_buf));
		switch(getSn_SR(sock))																/*获取socket的状态*/
		{
			case SOCK_INIT:				  													/*socket初始化完成*/
				if( connect(sock, FTP_SERVER_IP ,FTP_Server_CMD_Port)==0)
	            printf("< Fail : Socket Connect >\r\n");
			break;
			case SOCK_ESTABLISHED:															/*socket连接建立*/
				if(getSn_IR(sock)& Sn_IR_CON)
					{
	            		setSn_IR(sock, Sn_IR_CON);											/*Sn_IR的第0位置1*/
					}
	            len=getSn_RX_RSR(sock);														/*len为已接收数据的大小*/
				if(len>0)
					{
	            		recv(sock,cmd_buf,len);												/*W5500接收来自SeverServer的数据*/
						for( idx=0; idx < len; idx++)
							{
								if( idx ==0)
	                			printf("\r\n");
	                    		printf("%c",cmd_buf[idx]);
							}
					
						switch( status = Parse_FTPServer_Statuscode(cmd_buf))
							{
								case R_220:													// 新用户服务已就绪Service ready for new user.
									printf("\r\nInput your User ID > ");
									GetInputString(msg);
									Send_USER_ID(sock,msg);
								break;
								case R_331:													// 用户名字已验证,需要密码
									printf("\r\nInput your Password > ");
									GetInputString(msg);
									Send_PW(sock,msg);
								break;
								case R_230:													//User logged in, proceed用户已登陆,处理中
			   						printf("\r\nUser logged in, proceed");
			   						close(FTP_CLIENT_DTP_SOCK);
			   						Send_Port(sock, IP, DTP_Port);
								break;
								case R_200:									 				//'Generic' command ok命令正确
			   						printf("\r\nInput FTP CMD > ");
			     					GetInputString(msg);
			    					Send_cmd(sock, msg);
								break;
								case R_150:													//File Status文件状态正确:打开数据链接
			     					bRunDTP =1;
								break;
								case R_250:
			      					close(FTP_CLIENT_DTP_SOCK);
			     					Send_Port(sock, IP, DTP_Port);
								break;
				/* Closing data connection关闭数据链接.  File transfer/abort successful ,文件传输终止/完成*/
								case R_226:								 					
			   						close(FTP_CLIENT_DTP_SOCK);
			    					Send_Port(sock, IP, DTP_Port);
								break;
								case R_425:
			    					close(FTP_CLIENT_DTP_SOCK);
			    					Send_Port(sock, IP, DTP_Port);
								break;
								default:
			 						printf("\r\nDefault Status = %d",(uint16)status);
			   						JumpAddress =*(volatileunsignedlong*)(ApplicationAddress +4);//复位程序reset app
			   						Jump_To_Application =(pFunction) JumpAddress;
			   						Jump_To_Application();
									while(1);
							}
					}
			break;
			case SOCK_CLOSE_WAIT:																/*socket等待关闭状态*/
			break;
			case SOCK_CLOSED:				  													/*socket关闭*/
	         	printf("\r\n%d : FTP Client Start.\r\n",(u16)sock);
	           	socket(sock,Sn_MR_TCP,any_port++,Sn_MR_ND);										/*初始化TCP socket*/
			break;
		}
	}
	

上面的程序是FTP客户端会话通道的程序,在客户端与FTP成功连接后,会收到FTP服务器发来的220响应码,表示服务器已经准备就绪,可以接受用户登录。在使用Send_USER_ID() 和Send_PW()函数执行完登录流程后,FTP客户端使用Send_Port()主动向服务器发送自己的数据通道的端口号。最后等待用户的FTP指令,并使用Send_cmd()函数执行相应指令。

	void ftp_client_DTP(uint8 sock)
	{
		int len;
		int idx;
		switch(getSn_SR(sock))														/*获取socket的状态*/
			{
				case SOCK_INIT:
	        		listen(sock);
	        		bRunDTP =1;
				break;
				case SOCK_ESTABLISHED:
					if(getSn_IR(sock)& Sn_IR_CON)
						{
	           				setSn_IR(sock, Sn_IR_CON);								/*Sn_IR的第0位置为1*/
						}
	            	len=getSn_RX_RSR(sock);											/*len为已接收数据的大小*/
					if(len>0)
						{
	                		recv(sock,data_buf,len);								/*W5500接收来自SeverServer的数据*/
							for( idx =0; idx < len; idx++)
								{
									if( idx ==0)
		                			printf("\r\n");
		                			printf("%c",data_buf[idx]);
								}
						}
					if(bRunDTP)
						{
	                		bRunDTP =0;
							#ifdef Debug                
	                		printf("< FTP_DTP Connect OK>\r\n");
							#endif
						}
	
					if( bRunPutFile )
						{
	                		bRunPutFile =0;
	                		send(sock,(void*)PUTFILE, strlen(PUTFILE));
	                		close(sock);
						}
				break;
				case SOCK_CLOSE_WAIT: 											/*防止对方关闭连接,而数据还未接收完*/
	            	len=getSn_RX_RSR(sock);										/*len为已接收数据的大小*/
					if(len>0)
						{
	            			recv(sock,data_buf,len);							/*W5500接收来自Server的数据*/				
							for( idx =0; idx < len; idx++
								{
									if( idx ==0)
		                			printf("\r\n");
		                			printf("%c",data_buf[idx]);
								}
						}
					if( bRunPutFile )
						{
	                		bRunPutFile =0;
	                		send(sock,(void*)PUTFILE, strlen(PUTFILE));
						}
	           		close(sock);
				break;
				case SOCK_CLOSED:
	        		socket(sock,Sn_MR_TCP,DTP_Port++,Sn_MR_ND);
					if( DTP_Port >70000)
	            	DTP_Port = FTP_Client_TCPS_Port;
				break;
			}
	}
	

上面的程序是FTP客户端的数据通道程序,该socket一直监听,等待FTP服务器的连接。如果接到数据,会通过串口打印出来,如果有发送请求,将测试字符串发送给服务器。在完成下载安装后, 我们添加一个账户(本节使用的账户是wiznet,密码123456),并为这个账户添加一个文件夹,并勾选文件所有操作。点击确定后,这个账户就可以被连接了。如图1至3所示。


图1:在FileZilla Server上创建用户


图2:为新建的用户添加文件夹


图3:为文件夹添加属性

将程序下载到W5500EVB后,运行程序,在串口调试助手上可以看到输入提示,按照提示使用上面介绍的指令进行测试。
  1. 请录入账户,在串口端按照提示输入用户名和密码,如图4所示。登陆录成功后客户端主动发送数据通道的端口信息。如图5所示。

  2. 图4:在串口端发送录入信息

    图5:FileZilla Server上显示登陆成功
  3. 测试ls指令,可见test下的两个文件夹下的两个文件夹文件夹被列了出来。如图6所示。

  4. 图6:在串口发送ls指令
  5. 测试cd指令,打开music文件夹,并列出该文件夹下文件,如图7所示。

  6. 图7:在串口发送cd指令
  7. 测试put指令,向主目录下新增一个文件newdoc.txt,并向其写入“This is the txt file for put cmd testing!!!!WIZnet!!!”,并使用ls指令列出新的文件,如8所示。

  8. 图8:添加新文件
  9. 测试get指令,将新增的文件里的内容读回,可见刚才写入的内容已经读到了。如图9所示。

    图9:获取新文件的内容
  10. 测试bye指令,输入bye指令后,W5500 EVB断开与FTP服务器的连接,并重启。如图10及图11所示。

图10:在发送bye指令后板卡重启

图11:FileZilla Server上显示断开连接


例程下载:【FTP Client】
编译环境:keil V5.11
硬件要求:W5500EVB