單芯片解決方案,開啟全新體驗——W55MH32 高性能以太網單片機
W55MH32是WIZnet重磅推出的高性能以太網單片機,它為用戶帶來前所未有的集成化體驗。這顆芯片將強大的組件集于一身,具體來說,一顆W55MH32內置高性能Arm? Cortex-M3核心,其主頻最高可達216MHz;配備1024KB FLASH與96KB SRAM,滿足存儲與數據處理需求;集成TOE引擎,包含WIZnet全硬件TCP/IP協議棧、內置MAC以及PHY,擁有獨立的32KB以太網收發緩存,可供8個獨立硬件socket使用。如此配置,真正實現了All-in-One解決方案,為開發者提供極大便利。
在封裝規格上,W55MH32 提供了兩種選擇:QFN100和QFN68。
W55MH32L采用QFN100封裝版本,尺寸為12x12mm,其資源豐富,專為各種復雜工控場景設計。它擁有66個GPIO、3個ADC、12通道DMA、17個定時器、2個I2C、5個串口、2個SPI接口(其中1個帶I2S接口復用)、1個CAN、1個USB2.0以及1個SDIO接口。如此豐富的外設資源,能夠輕松應對工業控制中多樣化的連接需求,無論是與各類傳感器、執行器的通信,還是對復雜工業協議的支持,都能游刃有余,成為復雜工控領域的理想選擇。同系列還有QFN68封裝的W55MH32Q版本,該版本體積更小,僅為8x8mm,成本低,適合集成度高的網關模組等場景,軟件使用方法一致。更多信息和資料請進入http://www.w5500.com/網站或者私信獲取。
此外,本W55MH32支持硬件加密算法單元,WIZnet還推出TOE+SSL應用,涵蓋TCP SSL、HTTP SSL以及 MQTT SSL等,為網絡通信安全再添保障。
為助力開發者快速上手與深入開發,基于W55MH32L這顆芯片,WIZnet精心打造了配套開發板。開發板集成WIZ-Link芯片,借助一根USB C口數據線,就能輕松實現調試、下載以及串口打印日志等功能。開發板將所有外設全部引出,拓展功能也大幅提升,便于開發者全面評估芯片性能。
若您想獲取芯片和開發板的更多詳細信息,包括產品特性、技術參數以及價格等,歡迎訪問官方網頁:http://www.w5500.com/,我們期待與您共同探索W55MH32的無限可能。
第十九章 W55MH32 FTP_Client示例
本篇文章,我們將詳細介紹如何在W55MH32芯片上面實現FTP協議的客戶端模式。并通過實戰例程,為大家講解如何在W55MH32上使用FTP協議的客戶端模式來訪問FTP服務器并下載文件。
該例程用到的其他網絡協議,例如DHCP,請參考相關章節。有關 W55MH32的初始化過程,請參考Network Install章節,這里將不再贅述。
1 FTP協議簡介
FTP(File Transfer Protocol,文件傳輸協議)是一種標準的網絡協議,用于在客戶端和服務器之間傳輸文件。FTP客戶端協議是基于 FTP協議實現的,用來指導客戶端如何與 FTP服務器通信,實現文件的上傳、下載、目錄操作等功能。由于 FTP最初是以明文傳輸設計的,不夠安全,在 FTP上加入 SSL/TLS加密層,可提供加密的控制連接和數據連接。以下是 FTP客戶端協議的主要內容和工作機制的介紹。
2 FTP協議特點
基于 TCP傳輸:
FTP使用兩個 TCP連接:控制連接(端口 21)和數據連接(端口 20或 PASV模式動態分配的端口),確保可靠的數據傳輸。
分離控制與數據:
控制連接用于發送命令和接收響應。
數據連接用于文件內容或目錄信息的傳輸。
支持多種傳輸模式:
主動模式(Active Mode):服務器主動連接客戶端的數據端口。
被動模式(Passive Mode):客戶端主動連接服務器提供的數據端口,解決 NAT防火墻限制。
支持多種文件操作:
文件上傳(STOR)、下載(RETR)、刪除(DELE)。
目錄操作(MKD、RMD、CWD、PWD)。
獲取文件列表(LIST、NLST)。
明文傳輸(傳統 FTP):
用戶名、密碼及數據以明文形式傳輸,不安全。
安全改進:FTPS(FTP Secure,基于 SSL/TLS)和 SFTP(Secure File Transfer Protocol,基于 SSH)。
靈活的用戶認證機制:
支持匿名登錄(匿名用戶可通過 email作為密碼)。
支持認證用戶名和密碼。
3 FTP協議應用場景
接下來,我們了解下在W55MH32上,可以使用FTP協議完成哪些操作及應用呢?
版本升級: 嵌入式設備通過 FTP客戶端從遠程服務器下載最新的固件文件并執行升級。
日志下載:嵌入式設備在運行中生成日志、監測數據或用戶交互記錄,通過 FTP客戶端上傳到遠程服務器。
配置保存:用戶可以將配置文件上傳到FTP服務器進行備份或共享。
離線模式數據同步:嵌入式設備在脫機運行時本地保存數據,在網絡恢復后通過 FTP同步到服務器。
4 FTP工作流程
1.建立控制連接
客戶端初始化:客戶端啟動 FTP客戶端程序,指定要連接的 FTP服務器的地址和端口號(端口號為 21)。
TCP連接建立:客戶端通過 TCP協議向服務器的 21端口發起連接請求。服務器監聽該端口,接收到請求后,與客戶端建立起一條 TCP連接,這個連接被稱為控制連接,主要用于傳輸 FTP命令和服務器的響應信息。
身份驗證:連接建立后,服務器會提示客戶端輸入用戶名和密碼進行身份驗證。客戶端發送相應的用戶名和密碼信息到服務器,服務器驗證通過后,才允許客戶端進行后續操作。也有一些匿名 FTP服務器,允許用戶以 “anonymous” 作為用戶名,以電子郵件地址作為密碼進行登錄,提供公開的文件訪問服務。
2.傳輸模式選擇
客戶端和服務器在控制連接上協商數據傳輸模式,主要有兩種模式:
主動模式(PORT模式):客戶端通過控制連接告訴服務器自己的數據端口(客戶端隨機開放的一個端口),服務器使用 20端口主動連接客戶端的數據端口來傳輸數據。
被動模式(PASV模式):客戶端發送 PASV命令給服務器,服務器在控制連接上告知客戶端自己開放的一個臨時數據端口(通常是 1024以上的端口),然后客戶端使用自己的一個隨機端口連接服務器的這個臨時數據端口來傳輸數據。
3.數據傳輸
根據用戶的操作需求,通過數據連接進行文件或目錄相關操作:
上傳文件:客戶端向服務器發送 STOR(存儲)命令,然后通過數據連接將本地文件數據發送到服務器。服務器接收到數據后,將其存儲在指定的目錄下。
下載文件:客戶端向服務器發送 RETR(檢索)命令,請求下載服務器上的文件。服務器通過數據連接將文件數據發送給客戶端,客戶端接收數據并將其保存到本地指定位置。
目錄操作:客戶端還可以發送諸如 LIST(列出目錄內容)、CWD(更改工作目錄)、MKD(創建目錄)、RMD(刪除目錄)等命令,服務器執行相應操作,并通過控制連接返回操作結果。執行這些命令時,若需要傳輸目錄列表等數據,也會通過數據連接進行傳輸。
4.關閉連接
數據連接關閉:在完成文件傳輸或其他操作后,數據連接會被關閉。如果還有其他操作需要進行,客戶端和服務器可以根據需要重新建立數據連接。
控制連接關閉:當客戶端完成所有操作后,會向服務器發送 QUIT命令,服務器接收到該命令后,會關閉控制連接。至此,客戶端與服務器之間的 FTP會話結束。
5主動模式與被動模式詳解
主動模式(Active Mode):
客戶端打開一個端口并監聽。
客戶端通過控制連接告訴服務器自己的 IP和端口。
服務器主動連接到客戶端指定的端口傳輸數據。
被動模式(Passive Mode):
客戶端通過控制連接請求被動模式。
服務器打開一個隨機端口并通過控制連接告知客戶端。
客戶端主動連接到服務器指定的端口傳輸數據。
優缺點對比:
主動模式更適合在服務器端網絡無防火墻限制的環境。
被動模式更適合客戶端在 NAT或防火墻后的情況。
6 FTP客戶端實現功能
FTP客戶端的核心功能包括:
連接到 FTP服務器。
用戶身份認證。
上傳和下載文件。
瀏覽和管理服務器目錄結構。
支持主動模式和被動模式的切換。
提供斷點續傳功能(某些高級客戶端)。
7 FTP報文解析
FTP報文分為命令和響應報文,命令報文用于發送操作請求,響應報文用于返回結果。
命令報文格式為“<命令> <參數>rn”,字段解釋如下:
<命令>:FTP命令(如 USER、PASS)。
<參數>:命令的附加信息(如用戶名、文件名)。
例如“USER usernamern”。常見的命令包括登錄 (USER, PASS)、文件操作 (RETR, STOR)、目錄操作 (LIST, CWD)等。每個 FTP報文由命令或響應代碼、狀態碼及附加數據組成,狀態碼用于指示操作結果。
以下是 FTP常見命令:
USER:提供用戶名進行身份驗證。
PASS:提供密碼進行身份驗證。
CWD:更改當前工作目錄。
PWD:顯示當前工作目錄。
LIST:列出目錄下的文件和子目錄。
RETR:從服務器下載文件。
STOR:上傳文件到服務器。
DELE:刪除指定文件。
MKD:創建新目錄。
RMD:刪除目錄。
QUIT:終止會話并退出。
TYPE:設置文件傳輸類型(ASCII或 Binary)。
PORT:指定數據連接的端口。
PASV:啟用被動模式,服務器指定端口供客戶端連接。
響應報文格式為“<狀態碼> <說明文字>rn”,字段解釋如下:
<狀態碼>:三位數字表示狀態。
<說明文字>:狀態的文字描述。
例如“230 User logged in, proceed.rn”。以下是FTP常見的響應碼:
1xx(信息性響應):主要是提供一些初步的信息,通常表示服務器正在處理請求,還沒有完成操作。
2xx(成功響應):表示命令成功執行。這是客戶端最希望看到的響應類型之一,說明請求的操作(如登錄、文件傳輸等)順利完成。
3xx(補充信息響應):表示服務器需要一些額外的信息才能完成操作。通常是在身份驗證或者文件定位等過程中出現。
4xx(暫時錯誤響應):表示客戶端的請求有問題,但錯誤是暫時的,可能通過一些調整(如重新發送請求等)可以解決。
5xx(永久性錯誤響應):表示客戶端的請求存在錯誤,并且這個錯誤是比較嚴重的,很難通過簡單的調整來糾正。
接著我們來看看FTP獲取目錄的報文示例:
客戶端建立TCP連接到服務器的21端口
服務器返回:220 Welcome to FTP Serverrn
客戶端發送:USER wiznetrn
服務器返回:331 User wiznet OK.Password requiredrn
客戶端發送:PASS wiznetrn
服務器返回:230 User logged inrn
客戶端發送PORT 192,168,1,5,20,100rn(主動模式,192,168,1,5是客戶端的地址,20,100是客戶端期望的端口號20*256+100=5260)
服務器返回:200 PORT command successfulrn
客戶端發送:LISTrn(DIR命令,獲取當前目錄的文件信息)
服務器回復:150 Opening ASCII mode data connection for file listrn
服務器像客戶端期望的端口號發起TCP連接,并傳輸目錄信息,傳輸完成后關閉TCP連接。
客戶端發送:QUITrn(退出FTP會話)
服務器回復:221 Goodbyern
8實現過程
接下來,我們看看如何在W55MH32上實現FTP協議Client模式。
注意:測試實例需要PC端和W55MH32處于同一網段。
步驟一:FTP Client模式初始化
void ftpc_init(uint8_t *src_ip, uint8_t sn_ctrl, uint8_t sn_data) { ftpc.dsock_mode = ACTIVE_MODE; local_ip.cVal[0] = src_ip[0]; local_ip.cVal[1] = src_ip[1]; local_ip.cVal[2] = src_ip[2]; local_ip.cVal[3] = src_ip[3]; local_port = 35000; socket_ctrl = sn_ctrl; socket_data = sn_data; strcpy(ftpc.workingdir, "/"); socket(socket_ctrl, Sn_MR_TCP, FTP_destport, 0x0); }
ftpc_init()函數的主要作用是初始化FTP客戶端的配置和狀態,包括設置傳輸模式、本地IP地址和端口、socket、工作目錄,并創建用于命令傳輸的控制socket。
步驟二:在主循環中運行ftpc_run()函數
while (1) { ftpc_run(ethernet_buf); }
ftpc_run()函數如下所示:
uint8_t ftpc_run(uint8_t *dbuf) { uint16_t size = 0; long ret = 0; #if defined(F_FILESYSTEM) uint32_t send_byte; #endif uint32_t recv_byte; uint32_t blocklen; uint32_t remain_filesize; uint32_t remain_datasize; uint8_t dat[50] = { 0, }; switch (getSn_SR(socket_ctrl)) { case SOCK_ESTABLISHED: if (!connect_state_control_ftpc) { printf("%d:FTP Connectedrn", socket_ctrl); strcpy(ftpc.workingdir, "/"); connect_state_control_ftpc = 1; } if (gMenuStart) { gMenuStart = 0; printf("rn----------------------------------------rn"); printf("Press menu keyrn"); printf("----------------------------------------rn"); printf("1> View FTP Server Directoryrn"); printf("2> View My Directoryrn"); printf("3> Sets the type of file to be transferred. Current state : %srn", (ftpc.type == ASCII_TYPE) ? "Ascii" : "Binary"); printf("4> Sets Data Connection. Current state : %srn", (ftpc.dsock_mode == ACTIVE_MODE) ? "Active" : "Passive"); printf("5> Put File to Serverrn"); printf("6> Get File from Serverrn"); #if defined(F_FILESYSTEM) printf("7> Delete My Filern"); #endif printf("----------------------------------------rn"); while (1) { memset(gMsgBuf, 0, sizeof(gMsgBuf)); scanf("%s", gMsgBuf); if (gMsgBuf[0] == '1') { if (ftpc.dsock_mode == PASSIVE_MODE) { sprintf((char *)dat, "PASVrn"); send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); Command.First = f_dir; break; } else { wiz_NetInfo gWIZNETINFO; ctlnetwork(CN_GET_NETINFO, (void *)&gWIZNETINFO); sprintf((char *)dat, "PORT %d,%d,%d,%d,%d,%drn", gWIZNETINFO.ip[0], gWIZNETINFO.ip[1], gWIZNETINFO.ip[2], gWIZNETINFO.ip[3], (uint8_t)(local_port >> 8), (uint8_t)(local_port & 0x00ff)); send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); Command.First = f_dir; gModeActivePassiveflag = 1; break; } } else if (gMsgBuf[0] == '5') { if (ftpc.dsock_mode == PASSIVE_MODE) { sprintf((char *)dat, "PASVrn"); send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); Command.First = f_put; break; } else { wiz_NetInfo gWIZNETINFO; ctlnetwork(CN_GET_NETINFO, (void *)&gWIZNETINFO); sprintf((char *)dat, "PORT %d,%d,%d,%d,%d,%drn", gWIZNETINFO.ip[0], gWIZNETINFO.ip[1], gWIZNETINFO.ip[2], gWIZNETINFO.ip[3], (uint8_t)(local_port >> 8), (uint8_t)(local_port & 0x00ff)); send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); Command.First = f_put; gModeActivePassiveflag = 1; break; } } else if (gMsgBuf[0] == '6') { if (ftpc.dsock_mode == PASSIVE_MODE) { sprintf((char *)dat, "PASVrn"); send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); Command.First = f_get; break; } else { wiz_NetInfo gWIZNETINFO; ctlnetwork(CN_GET_NETINFO, (void *)&gWIZNETINFO); sprintf((char *)dat, "PORT %d,%d,%d,%d,%d,%drn", gWIZNETINFO.ip[0], gWIZNETINFO.ip[1], gWIZNETINFO.ip[2], gWIZNETINFO.ip[3], (uint8_t)(local_port >> 8), (uint8_t)(local_port & 0x00ff)); send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); Command.First = f_get; gModeActivePassiveflag = 1; break; } } else if (gMsgBuf[0] == '2') { #if defined(F_FILESYSTEM) scan_files(ftpc.workingdir, dbuf, (int *)&size); printf("rn%srn", dbuf); #else if (strncmp(ftpc.workingdir, "/$Recycle.Bin", sizeof("/$Recycle.Bin")) != 0) size = sprintf((char *)dbuf, "drwxr-xr-x 1 ftp ftp 0 Dec 31 2014 $Recycle.Binrn-rwxr-xr-x 1 ftp ftp 512 Dec 31 2014 test.txtrn"); printf("rn%srn", dbuf); #endif gMenuStart = 1; break; } else if (gMsgBuf[0] == '3') { printf("1> ASCIIrn"); printf("2> BINARYrn"); while (1) { memset(gMsgBuf, 0, sizeof(gMsgBuf)); scanf("%s", gMsgBuf); if (gMsgBuf[0] == '1') { sprintf((char *)dat, "TYPE %crn", TransferAscii); ftpc.type = ASCII_TYPE; send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); break; } else if (gMsgBuf[0] == '2') { sprintf((char *)dat, "TYPE %crn", TransferBinary); ftpc.type = IMAGE_TYPE; send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); break; } else if (gMsgBuf[0] != 0x00) { printf("rnRetry...rn"); } } break; } else if (gMsgBuf[0] == '4') { printf("1> ACTIVErn"); printf("2> PASSIVErn"); while (1) { memset(gMsgBuf, 0, sizeof(gMsgBuf)); scanf("%s", gMsgBuf); if (gMsgBuf[0] == '1') { ftpc.dsock_mode = ACTIVE_MODE; break; } else if (gMsgBuf[0] == '2') { ftpc.dsock_mode = PASSIVE_MODE; break; } else if (gMsgBuf[0] != 0x00) { printf("rnRetry...rn"); } } gMenuStart = 1; break; } #if defined(F_FILESYSTEM) else if (msg_c == '7') { printf(">del filename?"); memset(gMsgBuf, 0, sizeof(gMsgBuf)); scanf("%s", gMsgBuf); sprintf((char *)dat, "STOR %srn", gMsgBuf); if (f_unlink((const char *)ftpc.filename) != 0) { printf("rnCould not delete.rn"); } else { printf("rnDeleted.rn"); } gMenuStart = 1; break; } #endif else if (gMsgBuf[0] != 0x00) { printf("rnRetry...rn"); } } } if (gDataSockReady) { gDataSockReady = 0; switch (Command.First) { case f_dir: sprintf((char *)dat, "LISTrn"); send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); break; case f_put: printf(">put file name?"); memset(gMsgBuf, 0, sizeof(gMsgBuf)); scanf("%s", gMsgBuf); sprintf((char *)dat, "STOR %srn", gMsgBuf); send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); break; case f_get: printf(">get file name?"); memset(gMsgBuf, 0, sizeof(gMsgBuf)); scanf("%s", gMsgBuf); sprintf((char *)dat, "RETR %srn", gMsgBuf); send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat)); break; default: printf("Command.First = defaultrn"); break; } } if ((size = getSn_RX_RSR(socket_ctrl)) > 0) { // Don't need to check SOCKERR_BUSY because it doesn't not occur. memset(dbuf, 0, _MAX_SS); if (size > _MAX_SS) size = _MAX_SS - 1; ret = recv(socket_ctrl, dbuf, size); dbuf[ret] = ''; if (ret != size) { if (ret == SOCK_BUSY) return 0; if (ret < 0) { printf("%d:recv() error:%ldrn", socket_ctrl, ret); close(socket_ctrl); return ret; } } printf("Rcvd Command: %srn", dbuf); proc_ftpc((char *)dbuf, size); } break; case SOCK_CLOSE_WAIT: printf("%d:CloseWaitrn", socket_ctrl); if ((ret = disconnect(socket_ctrl)) != SOCK_OK) return ret; printf("%d:Closedrn", socket_ctrl); break; case SOCK_CLOSED: printf("%d:FTPStartrn", socket_ctrl); if ((ret = socket(socket_ctrl, Sn_MR_TCP, FTP_destport, 0x0)) != socket_ctrl) { printf("%d:socket() error:%ldrn", socket_ctrl, ret); close(socket_ctrl); return ret; } break; case SOCK_INIT: printf("%d:Openedrn", socket_ctrl); if ((ret = connect(socket_ctrl, local_ip.cVal, FTP_destport)) != SOCK_OK) { printf("%d:Connect errorrn", socket_ctrl); return ret; } connect_state_control_ftpc = 0; printf("%d:Connectting...rn", socket_ctrl); break; default: break; } switch (getSn_SR(socket_data)) { case SOCK_ESTABLISHED: if (!connect_state_data_ftpc) { printf("%d:FTP Data socket Connectedrn", socket_data); connect_state_data_ftpc = 1; } if (gDataPutGetStart) { switch (Command.Second) { case s_dir: printf("dir waiting...rn"); if ((size = getSn_RX_RSR(socket_data)) > 0) { // Don't need to check SOCKERR_BUSY because it doesn't not occur. printf("okrn"); memset(dbuf, 0, _MAX_SS); if (size > _MAX_SS) size = _MAX_SS - 1; ret = recv(socket_data, dbuf, size); dbuf[ret] = ''; if (ret != size) { if (ret == SOCK_BUSY) return 0; if (ret < 0) { printf("%d:recv() error:%ldrn", socket_ctrl, ret); close(socket_data); return ret; } } printf("Rcvd Data:nr%snr", dbuf); gDataPutGetStart = 0; Command.Second = s_nocmd; } break; case s_put: printf("put waiting...rn"); if (strlen(ftpc.workingdir) == 1) sprintf(ftpc.filename, "/%s", (uint8_t *)gMsgBuf); else sprintf(ftpc.filename, "%s/%s", ftpc.workingdir, (uint8_t *)gMsgBuf); #if defined(F_FILESYSTEM) ftpc.fr = f_open(&(ftpc.fil), (const char *)ftpc.filename, FA_READ); if (ftpc.fr == FR_OK) { remain_filesize = ftpc.fil.fsize; printf("f_open return FR_OKrn"); do { memset(dbuf, 0, _MAX_SS); if (remain_filesize > _MAX_SS) send_byte = _MAX_SS; else send_byte = remain_filesize; ftpc.fr = f_read(&(ftpc.fil), (void *)dbuf, send_byte, (UINT *)&blocklen); if (ftpc.fr != FR_OK) { break; } printf("#"); send(socket_data, dbuf, blocklen); remain_filesize -= blocklen; } while (remain_filesize != 0); printf("rnFile read finishedrn"); ftpc.fr = f_close(&(ftpc.fil)); } else { printf("File Open Error: %drn", ftpc.fr); ftpc.fr = f_close(&(ftpc.fil)); } #else remain_filesize = strlen(ftpc.filename); do { memset(dbuf, 0, _MAX_SS); blocklen = sprintf((char *)dbuf, "%s", ftpc.filename); // Upload file content printf("########## dbuf:%srn", dbuf); send(socket_data, dbuf, blocklen); remain_filesize -= blocklen; } while (remain_filesize != 0); #endif gDataPutGetStart = 0; Command.Second = s_nocmd; disconnect(socket_data); break; case s_get: printf("get waiting...rn"); if (strlen(ftpc.workingdir) == 1) sprintf(ftpc.filename, "/%s", (uint8_t *)gMsgBuf); else sprintf(ftpc.filename, "%s/%s", ftpc.workingdir, (uint8_t *)gMsgBuf); #if defined(F_FILESYSTEM) ftpc.fr = f_open(&(ftpc.fil), (const char *)ftpc.filename, FA_CREATE_ALWAYS | FA_WRITE); if (ftpc.fr == FR_OK) { printf("f_open return FR_OKrn"); while (1) { if ((remain_datasize = getSn_RX_RSR(socket_data)) > 0) { while (1) { memset(dbuf, 0, _MAX_SS); if (remain_datasize > _MAX_SS) recv_byte = _MAX_SS; else recv_byte = remain_datasize; ret = recv(socket_data, dbuf, recv_byte); ftpc.fr = f_write(&(ftpc.fil), (const void *)dbuf, (UINT)ret, (UINT *)&blocklen); remain_datasize -= blocklen; if (ftpc.fr != FR_OK) { printf("f_write failedrn"); break; } if (remain_datasize <= 0) break; } if (ftpc.fr != FR_OK) { printf("f_write failedrn"); break; } printf("#"); } else { if (getSn_SR(socket_data) != SOCK_ESTABLISHED) break; } } printf("rnFile write finishedrn"); ftpc.fr = f_close(&(ftpc.fil)); gDataPutGetStart = 0; } else { printf("File Open Error: %drn", ftpc.fr); } #else while (1) { if ((remain_datasize = getSn_RX_RSR(socket_data)) > 0) { while (1) { memset(dbuf, 0, _MAX_SS); if (remain_datasize > _MAX_SS) recv_byte = _MAX_SS; else recv_byte = remain_datasize; ret = recv(socket_data, dbuf, recv_byte); printf("########## dbuf:%srn", dbuf); remain_datasize -= ret; if (remain_datasize <= 0) break; } } else { if (getSn_SR(socket_data) != SOCK_ESTABLISHED) break; } } gDataPutGetStart = 0; Command.Second = s_nocmd; #endif break; default: printf("Command.Second = defaultrn"); break; } } break; case SOCK_CLOSE_WAIT: printf("%d:CloseWaitrn", socket_data); if ((size = getSn_RX_RSR(socket_data)) > 0) { // Don't need to check SOCKERR_BUSY because it doesn't not occur. ret = recv(socket_data, dbuf, size); dbuf[ret] = ''; if (ret != size) { if (ret == SOCK_BUSY) return 0; if (ret < 0) { printf("%d:recv() error:%ldrn", socket_ctrl, ret); close(socket_data); return ret; } } printf("Rcvd Data:nr%snr", dbuf); } if ((ret = disconnect(socket_data)) != SOCK_OK) return ret; printf("%d:Closedrn", socket_data); break; case SOCK_CLOSED: if (ftpc.dsock_state == DATASOCK_READY) { if (ftpc.dsock_mode == PASSIVE_MODE) { printf("%d:FTPDataStart, port : %drn", socket_data, local_port); if ((ret = socket(socket_data, Sn_MR_TCP, local_port, 0x0)) != socket_data) { printf("%d:socket() error:%ldrn", socket_data, ret); close(socket_data); return ret; } local_port++; if (local_port > 50000) local_port = 35000; } else { printf("%d:FTPDataStart, port : %drn", socket_data, local_port); if ((ret = socket(socket_data, Sn_MR_TCP, local_port, 0x0)) != socket_data) { printf("%d:socket() error:%ldrn", socket_data, ret); close(socket_data); return ret; } local_port++; if (local_port > 50000) local_port = 35000; } ftpc.dsock_state = DATASOCK_START; } break; case SOCK_INIT: printf("%d:Openedrn", socket_data); if (ftpc.dsock_mode == ACTIVE_MODE) { if ((ret = listen(socket_data)) != SOCK_OK) { printf("%d:Listen errorrn", socket_data); return ret; } gDataSockReady = 1; printf("%d:Listen okrn", socket_data); } else { if ((ret = connect(socket_data, remote_ip.cVal, remote_port)) != SOCK_OK) { printf("%d:Connect errorrn", socket_data); return ret; } gDataSockReady = 1; } connect_state_data_ftpc = 0; break; default: break; } return 0; }
在這個函數中,會分別執行兩個TCP狀態機,一個用于FTP控制,一個用于FTP數據傳輸,FTP數據傳輸的狀態機,僅當要進行數據傳輸時(上傳或下載文件)才會開啟。
首先看到FTP控制的狀態機,大致流程如下圖所示:
當用戶在選項菜單中選擇不同的選項時,系統會根據所選選項發送相應的操作指令。這些操作指令將被發送至服務器,隨后服務器會返回相應的響應內容。在proc_ftpc()函數中,程序會根據服務器返回的響應碼來判斷操作是否成功,并依據響應碼的具體情況執行下一步操作。一旦相應操作完成,程序將自動返回選項菜單,以便用戶繼續進行后續操作。
9運行結果
燒錄例程運行后,首先進行了PHY鏈路檢測,然后是通過DHCP獲取網絡地址并打印網絡地址信息,打印出FTP連接信息,并提示輸入信息進行連接,如下圖所示:
FileZilla Server Interface是 FileZilla Server的圖形用戶界面(GUI),用于管理和配置 FileZilla Server。FileZilla Server是一款開源的、跨平臺的 FTP和 FTP over TLS (FTPS)服務器,它常用于文件共享和數據傳輸。下載鏈接:服務端 - FileZilla中文網。下載、安裝完成后打開。
第一步:建立服務器,設置一個賬戶(用戶名:wiznet密碼:123456)
第二步:添加一個共享文件夾,用于客戶端和服務器的文件操作使用
第三步:在串口助手界面根據串口消息提示Rcvd Command: 220-FileZilla Server ????????? 0.9.60 beta先輸入用戶名,再輸入密碼(注意:發送的內容的結尾需要帶上回車換行符),然后串口就會發送6條選項
第四步:我們選擇6,從服務器獲取文件
第五步:然后輸入需要獲取文件的名字,如圖所示,成功獲取文件內容
其他操作類似,這里就不在一一進行講解。
10總結
本文講解了如何在 W55MH32芯片上實現 FTP協議的客戶端模式,通過實戰例程展示了使用該客戶端模式訪問 FTP服務器并下載文件的過程,涵蓋 FTP客戶端模式初始化、在主循環中運行相關函數實現與服務器交互等關鍵步驟。文章詳細介紹了 FTP協議的概念、特點、應用場景、工作流程、主動與被動模式、客戶端功能、報文解析,幫助讀者理解其在文件傳輸中的實際應用價值。
下一篇文章將聚焦 WOL(Wake-on-LAN)網絡喚醒功能,解析其核心原理及在網絡設備管理中的應用,同時講解如何在W55MH32上實現 WOL功能,敬請期待!
WIZnet是一家無晶圓廠半導體公司,成立于 1998年。產品包括互聯網處理器 iMCU?,它采用 TOE(TCP/IP卸載引擎)技術,基于獨特的專利全硬連線 TCP/IP。iMCU?面向各種應用中的嵌入式互聯網設備。
WIZnet在全球擁有 70多家分銷商,在香港、韓國、美國設有辦事處,提供技術支持和產品營銷。
香港辦事處管理的區域包括:澳大利亞、印度、土耳其、亞洲(韓國和日本除外)。
審核編輯 黃宇
-
單片機
+關注
關注
6068文章
45011瀏覽量
651228 -
以太網
+關注
關注
41文章
5684瀏覽量
176261 -
Client
+關注
關注
0文章
13瀏覽量
9083
發布評論請先 登錄
第十九章 ADC——電壓采集

第二章 W55MH32 DHCP示例

第三章 W55MH32 TCP Client示例

第五章 W55MH32 UDP示例

第九章 W55MH32 HTTP Server示例

第十章 W55MH32 SNTP示例

第十一章 W55MH32 SMTP示例

第十二章 W55MH32 NetBIOS示例

第十三章 W55MH32 UPnP端口轉發示例

第十四章 W55MH32 TFTP示例

第十五章 W55MH32 SNMP示例

第十六章 W55MH32 PING示例

第十八章 W55MH32 FTP_Server示例

第二十六章 W55MH32?上位機搜索和配置示例

第二十九章 W55MH32 Modbus_TCP_Server示例

評論