#W5500以太網控制芯片相關文章鏈接#
上一篇:
W5500以太網控制器芯片(四):實現DNS功能
使用w5500通過以太網口可以實現FTP功能。首先要了解FTP的基本命令和通訊方式。
FTP的本質是TCP連接,所以我們要實現一個TCP客戶端,然后實現一些指定的FTP命令的發送和解析。
FTP基本命令:
| 命令 | 描述 |
| ABOR | 中斷數據連接程序 |
| ACCT <account> | 系統特權帳號 |
| ALLO <bytes> | 為服務器上的文件存儲器分配字節 |
| APPE <filename> | 添加文件到服務器同名文件 |
| CDUP <dir path> | 改變服務器上的父目錄 |
| CWD <dir path> | 改變服務器上的工作目錄 |
| DELE <filename> | 刪除服務器上的指定文件 |
| HELP <command> | 返回指定命令信息 |
| LIST <name> | 如果是文件名列出文件信息,如果是目錄則列出文件列表 |
| MODE <mode> | 傳輸模式(S=流模式,B=塊模式,C=壓縮模式) |
| MKD <directory> | 在服務器上建立指定目錄 |
| NLST <directory> | 列出指定目錄內容 |
| NOOP | 無動作,除了來自服務器上的承認 |
| PASS <password> | 系統登錄密碼 |
| PASV | 請求服務器等待數據連接 |
| PORT <address> | IP 地址和兩字節的端口 ID |
| PWD | 顯示當前工作目錄 |
| QUIT | 從 FTP 服務器上退出登錄 |
| REIN | 重新初始化登錄狀態連接 |
| REST <offset> | 由特定偏移量重啟文件傳遞 |
| RETR <filename> | 從服務器上找回(復制)文件 |
| RMD <directory> | 在服務器上刪除指定目錄 |
| RNFR <old path> | 對舊路徑重命名 |
| RNTO <new path> | 對新路徑重命名 |
| SITE <params> | 由服務器提供的站點特殊參數 |
| SMNT <pathname> | 掛載指定文件結構 |
| STAT <directory> | 在當前程序或目錄上返回信息 |
| STOR <filename> | 儲存(復制)文件到服務器上 |
| STOU <filename> | 儲存文件到服務器名稱上 |
| STRU <type> | 數據結構(F=文件,R=記錄,P=頁面) |
| SYST | 返回服務器使用的操作系統 |
| TYPE <data type> | 數據類型(A=ASCII,E=EBCDIC,I=binary) |
| USER <username> | 系統登錄的用戶名 |
FTP基本響應碼:
| 響應代碼 | 解釋說明 |
| 110 | 新文件指示器上的重啟標記 |
| 120 | 服務器準備就緒的時間(分鐘數) |
| 125 | 打開數據連接,開始傳輸 |
| 150 | 打開連接 |
| 200 | 成功 |
| 202 | 命令沒有執行 |
| 211 | 系統狀態回復 |
| 212 | 目錄狀態回復 |
| 213 | 文件狀態回復 |
| 214 | 幫助信息回復 |
| 215 | 系統類型回復 |
| 220 | 服務就緒 |
| 221 | 退出網絡 |
| 225 | 打開數據連接 |
| 226 | 結束數據連接 |
| 227 | 進入被動模式(IP 地址、ID 端口) |
| 230 | 登錄因特網 |
| 250 | 文件行為完成 |
| 257 | 路徑名建立 |
| 331 | 要求密碼 |
| 332 | 要求帳號 |
| 350 | 文件行為暫停 |
| 421 | 服務關閉 |
| 425 | 無法打開數據連接 |
| 426 | 結束連接 |
| 450 | 文件不可用 |
| 451 | 遇到本地錯誤 |
| 452 | 磁盤空間不足 |
| 500 | 無效命令 |
| 501 | 錯誤參數 |
| 502 | 命令沒有執行 |
| 503 | 錯誤指令序列 |
| 504 | 無效命令參數 |
| 530 | 未登錄網絡 |
| 532 | 存儲文件需要帳號 |
| 550 | 文件不可用 |
| 551 | 不知道的頁類型 |
| 552 | 超過存儲分配 |
| 553 | 文件名不允許 |
通過發送和解析這些命令和響應碼,再加上TCP客戶端和服務器,就可以分別實現FTP客戶端和FTP服務器。
下面來實現一個FTP客戶端,下載升級文件:
一般FTP分主動和被動模式,在主動模式下,通常21端口是命令端口,20端口是數據端口。在被動模式下,數據端口不一定。一般客戶端可以先通知服務器,請求被動模式命令端口,然后再解析服務器的回復,得到被動端口,然后去連接,接收數據內容。
1、開始先定義一些參數:
//FTP服務socket通道
#define SOCK_FTP_CTRL 6
#define SOCK_FTP_DATA 7un_l2cval remote_ip;
uint16_t remote_port;
uint16_t local_port;
uint8_t connect_state_control_ftpc = 0;
uint8_t connect_state_data_ftpc = 0;
uint8_t gModeActivePassiveflag = 0;
uint8_t gMenuStart = 0;
uint8_t gDataSockReady = 0;
uint8_t gDataPutGetStart = 0;//FTP服務器地址
uint8_t FTP_destip[4] = {192, 1, 1, 1};
uint16_t FTP_destport = 0;//FTP數據傳輸緩沖區
uint8_t FTP_data_buf[1500] = {0};struct ftpc ftpc;
struct Command Command;//以太網獲取FTP狀態
uint8_t ether_ftp_sta = 0;//FTP文件大小
u32 ftp_file_size = 0;//FTP文件句柄
int ftp_fd_w = 0;void ftp_data_deal(u8 *buf, u16 len);void ftpc_init(void);uint8_t ftpc_run(void);char proc_ftpc(char *buf);int pportc(char *arg);
頭文件定義:
#ifndef __W5500_FTP_H
#define __W5500_FTP_H#include "bsp_include.h"#include <stdint.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>#include "w5500_socket.h"#define LINELEN 128#define _MAX_SS 1400/* FTP Responses */
#define R_150 150 /* File status ok; opening data conn */
#define R_200 200 /* 'Generic' command ok */
#define R_220 220 /* Service ready for new user. */
#define R_226 226 /* Closing data connection. File transfer/abort successful */
#define R_227 227 /* Entering passive mode (h1,h2,h3,h4,p1,p2) */
#define R_230 230 /* User logged in, proceed */
#define R_331 331 /* User name okay, need password. */#define TransferAscii 'A'
#define TransferBinary 'I'enum ftpc_type {ASCII_TYPE,IMAGE_TYPE,
};enum ftpc_datasock_state{DATASOCK_IDLE,DATASOCK_READY,DATASOCK_START
};enum ftpc_datasock_mode{PASSIVE_MODE,ACTIVE_MODE
};
enum CommandFirst {f_nocmd,f_dir,f_put,f_get,
};
enum CommandSecond {s_nocmd,s_dir,s_put,s_get,
};
struct Command {enum CommandFirst First;enum CommandSecond Second;
};
struct ftpc {uint8_t control; /* Control stream */uint8_t data; /* Data stream */enum ftpc_type type; /* Transfer type */enum ftpc_datasock_state dsock_state;enum ftpc_datasock_mode dsock_mode;char workingdir[LINELEN];char filename[LINELEN];
};typedef union _un_l2cval {uint32_t lVal;uint8_t cVal[4];
}un_l2cval;void Ethernet_FTP_service_deal(void);#endif
2、定義一個FTP服務,下載完成后退出
//初始化FTP Client
void ftpc_init(void)
{connect_state_control_ftpc = 0;connect_state_data_ftpc = 0;gModeActivePassiveflag = 0;gMenuStart = 0;gDataSockReady = 0;gDataPutGetStart = 0;ftp_file_size = 0;ftp_fd_w = 0;//創建FTP命令socketftpc.dsock_mode = PASSIVE_MODE;local_port = 35000;w5500_socket(SOCK_FTP_CTRL, Sn_MR_TCP, FTP_destport, 0x0);
}//以太網FTP服務處理
void Ethernet_FTP_service_deal(void)
{//下載初始化if (ether_ftp_sta == 0){ether_ftp_sta = 1;ftpc_init();}//下載完成if (ether_ftp_sta > 2){//關閉socket通道w5500_disconnect(SOCK_FTP_DATA);w5500_close(SOCK_FTP_DATA);w5500_disconnect(SOCK_FTP_CTRL);w5500_close(SOCK_FTP_CTRL);if (ether_ftp_sta == 3) //下載成功{ol_print(DEBUG_CHN, 0, "ETH FTP success\n");System_Sta.FTP_Sta = FTP_SUCCESS;}else if (ether_ftp_sta == 0xFF) //下載失敗{ol_print(DEBUG_CHN, 0, "ETH FTP fail\n");System_Sta.FTP_Sta = FTP_FAIL;}ether_ftp_sta = 0;return;}ftpc_run(); //FTP客戶端狀態機
}
3、實現FTP下載文件的命令發送流程
(1)、連接FTP服務器,提供用戶名密碼。
(2)、通知FTP服務器被動模式,解析被動模式端口(PASV),打開數據通道
(3)、查詢指定的文件在FTP服務器是否存在,獲取文件大小(LIST),數據通道斷開
(4)、再次請求數據通道端口(PASV),打開數據通道
(5)、分段讀取文件(RETR),讀取完成,數據通道斷開
(6)、比較讀取到的文件和之前查詢的長度是否一致
//FTP客戶端狀態機
uint8_t ftpc_run(void)
{uint16_t size = 0;long ret = 0;uint32_t recv_byte;uint32_t remain_datasize;uint8_t dat[50] = {0,};//FTP命令通道處理switch (getSn_SR(SOCK_FTP_CTRL)){case SOCK_ESTABLISHED:{//socket連接成功if (!connect_state_control_ftpc){ol_print(DEBUG_CHN, 0, "%d:FTP Connected\r\n", SOCK_FTP_CTRL);strcpy(ftpc.workingdir, "/");connect_state_control_ftpc = 1;}//有命令要執行if (gMenuStart){ol_print(DEBUG_CHN, 0, "FTP cmd start:%d\r\n", ether_ftp_sta);gMenuStart = 0;if (ether_ftp_sta == 1) //開始查詢文件基本信息{if (ftpc.dsock_mode == PASSIVE_MODE) //被動模式下載{sprintf((char *)dat, "PASV\r\n");w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));Command.First = f_dir;}else{wiz_NetInfo gWIZNETINFO;ctlnetwork(CN_GET_NETINFO, (void *)&gWIZNETINFO);sprintf((char *)dat, "PORT %d,%d,%d,%d,%d,%d\r\n", gWIZNETINFO.ip[0], gWIZNETINFO.ip[1], gWIZNETINFO.ip[2], gWIZNETINFO.ip[3], (uint8_t)(local_port >> 8), (uint8_t)(local_port & 0x00ff));w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));Command.First = f_dir;gModeActivePassiveflag = 1;}}else if (ether_ftp_sta == 2) //開始下載文件,需要重新分配一次端口{if (ftpc.dsock_mode == PASSIVE_MODE) //被動模式下載{sprintf((char *)dat, "PASV\r\n");w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));Command.First = f_get;}else{wiz_NetInfo gWIZNETINFO;ctlnetwork(CN_GET_NETINFO, (void *)&gWIZNETINFO);sprintf((char *)dat, "PORT %d,%d,%d,%d,%d,%d\r\n", gWIZNETINFO.ip[0], gWIZNETINFO.ip[1], gWIZNETINFO.ip[2], gWIZNETINFO.ip[3], (uint8_t)(local_port >> 8), (uint8_t)(local_port & 0x00ff));w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));Command.First = f_get;gModeActivePassiveflag = 1;}}}//數據通道就緒,可以開始發送FTP命令if (gDataSockReady){gDataSockReady = 0;switch (Command.First){case f_dir: //列出要下載的FTP文件信息// sprintf(dat, "MLSD\r\n");sprintf((char *)dat, "LIST %s\r\n", System_Para.Filename);w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));break;case f_get:ol_print(DEBUG_CHN, 0, ">get file name?%s\n", System_Para.Filename);sprintf((char *)dat, "RETR %s\r\n", System_Para.Filename);w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));break;}}//接收到FTP命令通道數據if ((size = getSn_RX_RSR(SOCK_FTP_CTRL)) > 0){// Don't need to check SOCKERR_BUSY because it doesn't not occur.memset(FTP_data_buf, 0, _MAX_SS);if (size > _MAX_SS)size = _MAX_SS - 1;ret = w5500_recv(SOCK_FTP_CTRL, FTP_data_buf, size);FTP_data_buf[ret] = '\0';if (ret != size){if (ret == SOCK_BUSY)return 0;if (ret < 0){ol_print(DEBUG_CHN, 0, "%d:recv() error:%ld\r\n", SOCK_FTP_CTRL, ret);w5500_close(SOCK_FTP_CTRL);//命令通道接收異常,關閉FTP下載ether_ftp_sta = 0xFF;return ret;}}ol_print(DEBUG_CHN, 0, "Rcvd Command: %s\r\n", FTP_data_buf);proc_ftpc((char *)FTP_data_buf); //處理命令}break;}case SOCK_CLOSE_WAIT: //SOCKET關閉等待{ol_print(DEBUG_CHN, 0, "%d:CloseWait\r\n", SOCK_FTP_CTRL);if ((ret = w5500_disconnect(SOCK_FTP_CTRL)) != SOCK_OK)return ret;ol_print(DEBUG_CHN, 0, "%d:Closed\r\n", SOCK_FTP_CTRL);break;}case SOCK_CLOSED: //SOCKET關閉狀態{ol_print(DEBUG_CHN, 0, "%d:FTPStart\r\n", SOCK_FTP_CTRL);if ((ret = w5500_socket(SOCK_FTP_CTRL, Sn_MR_TCP, FTP_destport, 0x0)) != SOCK_FTP_CTRL){ol_print(DEBUG_CHN, 0, "%d:socket() error:%ld\r\n", SOCK_FTP_CTRL, ret);w5500_close(SOCK_FTP_CTRL);return ret;}break;}case SOCK_INIT: //SOCKET初始化狀態{ol_print(DEBUG_CHN, 0, "%d:Opened\r\n", SOCK_FTP_CTRL);if ((ret = w5500_connect(SOCK_FTP_CTRL, FTP_destip, FTP_destport)) != SOCK_OK){ol_print(DEBUG_CHN, 0, "%d:Connect error\r\n", SOCK_FTP_CTRL);return ret;}connect_state_control_ftpc = 0;ol_print(DEBUG_CHN, 0, "%d:Connectting...\r\n", SOCK_FTP_CTRL);break;}}//FTP數據通道switch (getSn_SR(SOCK_FTP_DATA)){case SOCK_ESTABLISHED:{if (!connect_state_data_ftpc){ol_print(DEBUG_CHN, 0, "%d:FTP Data socket Connected\r\n", SOCK_FTP_DATA);connect_state_data_ftpc = 1;}//開始接收數據if (gDataPutGetStart){ol_print(DEBUG_CHN, 0, "%d:rec ftp data:%d\r\n", SOCK_FTP_DATA, Command.Second);switch (Command.Second){case s_dir: //查詢文件信息是小文本,發送完后數據通道會直接斷開,此段程序一般無法進入{while (1){if ((remain_datasize = getSn_RX_RSR(SOCK_FTP_DATA)) > 0){while (1){memset(FTP_data_buf, 0, _MAX_SS);if (remain_datasize > _MAX_SS)recv_byte = _MAX_SS;elserecv_byte = remain_datasize;ret = w5500_recv(SOCK_FTP_DATA, FTP_data_buf, recv_byte);if (ret > 0){ftp_data_deal(FTP_data_buf, ret);}else{//傳輸失敗,結束FTP下載ether_ftp_sta = 0xFF;return 0;}remain_datasize -= ret;if (remain_datasize <= 0)break;}}else{if (getSn_SR(SOCK_FTP_DATA) != SOCK_ESTABLISHED)break;}}gDataPutGetStart = 0;Command.Second = s_nocmd;break;}case s_get:{while (1){if ((remain_datasize = getSn_RX_RSR(SOCK_FTP_DATA)) > 0){while (1){memset(FTP_data_buf, 0, _MAX_SS);if (remain_datasize > _MAX_SS)recv_byte = _MAX_SS;elserecv_byte = remain_datasize;ret = w5500_recv(SOCK_FTP_DATA, FTP_data_buf, recv_byte);if (ret > 0){ftp_data_deal(FTP_data_buf, ret);}else{//傳輸失敗,結束FTP下載ether_ftp_sta = 0xFF;return 0;}remain_datasize -= ret;if (remain_datasize <= 0)break;}}else{if (getSn_SR(SOCK_FTP_DATA) != SOCK_ESTABLISHED)break;}}gDataPutGetStart = 0;Command.Second = s_nocmd;break;}}}break;}case SOCK_CLOSE_WAIT:{//對于較小的數據,一次傳輸后FTP數據通道會直接關閉,所以在SOCK_CLOSE_WAIT狀態下也要先接收一次數據if (gDataPutGetStart){ol_print(DEBUG_CHN, 0, "%d:rec ftp data wait:%d\r\n", SOCK_FTP_DATA, Command.Second);switch (Command.Second){case s_dir:{while (1){if ((remain_datasize = getSn_RX_RSR(SOCK_FTP_DATA)) > 0){while (1){memset(FTP_data_buf, 0, _MAX_SS);if (remain_datasize > _MAX_SS)recv_byte = _MAX_SS;elserecv_byte = remain_datasize;ret = w5500_recv(SOCK_FTP_DATA, FTP_data_buf, recv_byte);if (ret > 0){ftp_data_deal(FTP_data_buf, ret);}else{//傳輸失敗,結束FTP下載ether_ftp_sta = 0xFF;return 0;}remain_datasize -= ret;if (remain_datasize <= 0)break;}}else{if (getSn_SR(SOCK_FTP_DATA) != SOCK_ESTABLISHED)break;}}gDataPutGetStart = 0;Command.Second = s_nocmd;break;}case s_get:{while (1){if ((remain_datasize = getSn_RX_RSR(SOCK_FTP_DATA)) > 0){while (1){memset(FTP_data_buf, 0, _MAX_SS);if (remain_datasize > _MAX_SS)recv_byte = _MAX_SS;elserecv_byte = remain_datasize;ret = w5500_recv(SOCK_FTP_DATA, FTP_data_buf, recv_byte);if (ret > 0){ftp_data_deal(FTP_data_buf, ret);}else{//傳輸失敗,結束FTP下載ether_ftp_sta = 0xFF;return 0;}remain_datasize -= ret;if (remain_datasize <= 0)break;}}else{if (getSn_SR(SOCK_FTP_DATA) != SOCK_ESTABLISHED)break;}}gDataPutGetStart = 0;Command.Second = s_nocmd;break;}}}ol_print(DEBUG_CHN, 0, "%d:CloseWait\r\n", SOCK_FTP_DATA);if ((ret = w5500_disconnect(SOCK_FTP_DATA)) != SOCK_OK){//無法正常關閉數據通道,FTP失敗ether_ftp_sta = 0xFF;return 0;}else{//關閉數據通道成功,數據解析完畢,進入FTP下一步if (ether_ftp_sta == 1) //文件信息讀取結束{ether_ftp_sta = 2;gMenuStart = 1;}else if (ether_ftp_sta == 2) //文件下載結束,準備升級{ether_ftp_sta = 3;vfs_close(ftp_fd_w);//讀取文件大小ret = vfs_file_size("/fota/ftptext.pack");ol_print(DEBUG_CHN, 0, "FTP file size:%d,%d\n", ftp_file_size, ret);if (ftp_file_size != System_Para.Filesize){ether_ftp_sta = 0xFF;}ret = vfs_rename("/fota/ftptext.pack", "/fota/fota.pack");if (ret != 0){ether_ftp_sta = 0xFF;}}}ol_print(DEBUG_CHN, 0, "%d:Closed\r\n", SOCK_FTP_DATA);break;}case SOCK_CLOSED:{if (ftpc.dsock_state == DATASOCK_READY){if (ftpc.dsock_mode == PASSIVE_MODE){ol_print(DEBUG_CHN, 0, "%d:FTPDataStart, port : %d\r\n", SOCK_FTP_DATA, local_port);if ((ret = w5500_socket(SOCK_FTP_DATA, Sn_MR_TCP, local_port, 0x0)) != SOCK_FTP_DATA){ol_print(DEBUG_CHN, 0, "%d:socket() error:%ld\r\n", SOCK_FTP_DATA, ret);w5500_close(SOCK_FTP_DATA);return ret;}local_port++;if (local_port > 50000)local_port = 35000;}else{ol_print(DEBUG_CHN, 0, "%d:FTPDataStart, port : %d\r\n", SOCK_FTP_DATA, local_port);if ((ret = w5500_socket(SOCK_FTP_DATA, Sn_MR_TCP, local_port, 0x0)) != SOCK_FTP_DATA){ol_print(DEBUG_CHN, 0, "%d:socket() error:%ld\r\n", SOCK_FTP_DATA, ret);w5500_close(SOCK_FTP_DATA);return ret;}local_port++;if (local_port > 50000)local_port = 35000;}ftpc.dsock_state = DATASOCK_START;}break;}case SOCK_INIT:{ol_print(DEBUG_CHN, 0, "%d:Opened\r\n", SOCK_FTP_DATA);if (ftpc.dsock_mode == ACTIVE_MODE){if ((ret = w5500_listen(SOCK_FTP_DATA)) != SOCK_OK){OSI_LOGI(0, "w5500 FTP Listen error %d", SOCK_FTP_DATA);return ret;}gDataSockReady = 1;OSI_LOGI(0, "w5500 FTP Listen ok:%d", SOCK_FTP_DATA);}else{if ((ret = w5500_connect(SOCK_FTP_DATA, remote_ip.cVal, remote_port)) != SOCK_OK){ol_print(DEBUG_CHN, 0, "%d:Connect error\r\n", SOCK_FTP_DATA);return ret;}gDataSockReady = 1; //FTP數據通道開啟完成,準備執行命令}connect_state_data_ftpc = 0;break;}}return 0;
}
解析FTP相應碼:
//FTP服務器回復的協議解析
char proc_ftpc(char *buf)
{uint16_t Responses;uint8_t dat[30] = {0,};Responses = (buf[0] - '0') * 100 + (buf[1] - '0') * 10 + (buf[2] - '0');switch (Responses){case R_220: /* Service ready for new user. */ol_print(DEBUG_CHN, 0, "USER %s\n", System_Para.User);sprintf((char *)dat, "USER %s\r\n", System_Para.User);ol_print(DEBUG_CHN, 0, "\r\n");w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));break;case R_331: /* User name okay, need password. */ol_print(DEBUG_CHN, 0, "PASS %s\n", System_Para.Pass);sprintf((char *)dat, "PASS %s\r\n", System_Para.Pass);w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));break;case R_230: //登錄成功,通知FTP服務器使用ASCII通訊ol_print(DEBUG_CHN, 0, "\r\nlogged in, proceed\r\n");sprintf((char *)dat, "TYPE %c\r\n", TransferAscii);ftpc.type = ASCII_TYPE;w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));break;case R_200: //設置ASCII模式完成,服務器回復成功{if ((ftpc.dsock_mode == ACTIVE_MODE) && gModeActivePassiveflag){ftpc.dsock_state = DATASOCK_READY;gModeActivePassiveflag = 0;}else{gMenuStart = 1;}break;}case R_150: //命令發送成功,開始傳輸數據{switch (Command.First){case f_dir:Command.First = f_nocmd;Command.Second = s_dir;gDataPutGetStart = 1;break;case f_get:Command.First = f_nocmd;Command.Second = s_get;gDataPutGetStart = 1;break;}break;}case R_226: //數據通道結束連接gMenuStart = 1;break;case R_227: //被動模式設置成功,服務器返回被動模式數據端口{if (pportc(buf) == -1){ol_print(DEBUG_CHN, 0, "Bad port syntax\r\n");//轉換被動模式失敗,結束FTPether_ftp_sta = 0xFF;}else{ol_print(DEBUG_CHN, 0, "Go Open Data Sock...\r\n ");ftpc.dsock_mode = PASSIVE_MODE;ftpc.dsock_state = DATASOCK_READY; //開啟FTP數據通道}break;}default:OSI_LOGI(0, "w5500 FTP Default Status:%d", (uint16_t)Responses);gDataSockReady = 1;break;}return 1;
}
計算被動模式端口:
//計算被動模式連接端口
int pportc(char *arg)
{int i;char *tok = 0;strtok(arg, "(");for (i = 0; i < 4; i++){if (i == 0)tok = strtok(NULL, ",\r\n");elsetok = strtok(NULL, ",");remote_ip.cVal[i] = (uint8_t)atoi(tok);if (!tok){ol_print(DEBUG_CHN, 0, "bad pport : %s\r\n", arg);return -1;}}remote_port = 0;for (i = 0; i < 2; i++){tok = strtok(NULL, ",\r\n");remote_port <<= 8;remote_port += atoi(tok);if (!tok){ol_print(DEBUG_CHN, 0, "bad pport : %s\r\n", arg);return -1;}}ol_print(DEBUG_CHN, 0, "ip : %d.%d.%d.%d, port : %d\r\n", remote_ip.cVal[0], remote_ip.cVal[1], remote_ip.cVal[2], remote_ip.cVal[3], remote_port);return 0;
}
5、處理FTP接收數據
僅供參考,文件參數信息解析的是filezilla服務器的默認信息。文件原始數據的接收也要根據w5500設備所在的文件存儲類型(文件系統或者是無系統的FLASH)自行處理。
//查找下一空格后的第一個字符
s32 search_space(char *buf, u16 max_len)
{char *p = buf;u8 falg = 0, res = 0;while (falg < 2){if (*p == 0x20){falg = 1;}else{if (falg == 1){falg = 2;break;}}res++;if (res > max_len){return 0xff;}p++;}return res;
}//接收處理FTP文件、命令協議
void ftp_data_deal(u8 *buf, u16 len)
{u8 pos = 0;u32 file_size = 0;s32 ret;//-r--r--r-- 1 ftp ftp 89344 May 20 2021 FTP.bin//查詢文件信息解析文件大小if (Command.Second == s_dir){ol_print(DEBUG_CHN, 0, "FTP file info,%d:%s", len, buf);//解析過第一個空格 文件權限pos = search_space((char *)buf, len);if (pos == 0xFF)return;buf += pos;//解析第二個空格 文件硬連接數len = len - pos;pos = search_space((char *)buf, len);if (pos == 0xFF)return;buf += pos;//解析第三個空格 文件所有人len = len - pos;pos = search_space((char *)buf, len);if (pos == 0xFF)return;buf += pos;//解析第四個空格 文件所有組len = len - pos;pos = search_space((char *)buf, len);if (pos == 0xFF)return;buf += pos;//解析文件大小file_size = Str2Num((char *)buf, &pos);ol_print(DEBUG_CHN, 0, "FTP file size:%d\r\n", file_size);System_Para.Filesize = file_size;}else if (Command.Second == s_get) //下載文件{OSI_LOGI(0, "w5500 FTP page size:%d", len);//創建文件if (ftp_file_size == 0){//打開文件vfs_mkdir("/fota", 0);ftp_fd_w = vfs_open("/fota/ftptext.pack", O_CREAT | O_RDWR | O_TRUNC);if (ftp_fd_w < 0){ol_print(DEBUG_CHN, 0, "FTP file open fail\r\n");vfs_close(ftp_fd_w);ether_ftp_sta = 0xFF;return;}}//傳輸文件ret = vfs_write(ftp_fd_w, buf, len);if (ret <= 0){ol_print(DEBUG_CHN, 0, "FTP file write fail,%d\r\n", ftp_file_size);vfs_close(ftp_fd_w);ether_ftp_sta = 0xFF;return;}else if (ret < len) //一次未傳輸完成{pos = len - ret;ret = vfs_write(ftp_fd_w, buf, pos);if (ret != pos){ol_print(DEBUG_CHN, 0, "FTP file write fail,%d\r\n", ftp_file_size);vfs_close(ftp_fd_w);ether_ftp_sta = 0xFF;return;}}else{ftp_file_size += len;}OSI_LOGI(0, "w5500 FTP total size:%d", ftp_file_size);}
}
總結
以上是生活随笔為你收集整理的W5500以太网控制器芯片(五):实现FTP客户端的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。