單芯片解決方案,開(kāi)啟全新體驗(yàn)——W55MH32 高性能以太網(wǎng)單片機(jī)
W55MH32是WIZnet重磅推出的高性能以太網(wǎng)單片機(jī),它為用戶帶來(lái)前所未有的集成化體驗(yàn)。這顆芯片將強(qiáng)大的組件集于一身,具體來(lái)說(shuō),一顆W55MH32內(nèi)置高性能Arm? Cortex-M3核心,其主頻最高可達(dá)216MHz;配備1024KB FLASH與96KB SRAM,滿足存儲(chǔ)與數(shù)據(jù)處理需求;集成TOE引擎,包含WIZnet全硬件TCP/IP協(xié)議棧、內(nèi)置MAC以及PHY,擁有獨(dú)立的32KB以太網(wǎng)收發(fā)緩存,可供8個(gè)獨(dú)立硬件socket使用。如此配置,真正實(shí)現(xiàn)了All-in-One解決方案,為開(kāi)發(fā)者提供極大便利。
在封裝規(guī)格上,W55MH32提供了兩種選擇:QFN100和QFN68。
W55MH32L采用QFN100封裝版本,尺寸為12x12mm,其資源豐富,專為各種復(fù)雜工控場(chǎng)景設(shè)計(jì)。它擁有66個(gè)GPIO、3個(gè)ADC、12通道DMA、17個(gè)定時(shí)器、2個(gè)I2C、5個(gè)串口、2個(gè)SPI接口(其中1個(gè)帶I2S接口復(fù)用)、1個(gè)CAN、1個(gè)USB2.0以及1個(gè)SDIO接口。如此豐富的外設(shè)資源,能夠輕松應(yīng)對(duì)工業(yè)控制中多樣化的連接需求,無(wú)論是與各類傳感器、執(zhí)行器的通信,還是對(duì)復(fù)雜工業(yè)協(xié)議的支持,都能游刃有余,成為復(fù)雜工控領(lǐng)域的理想選擇。同系列還有QFN68封裝的W55MH32Q版本,該版本體積更小,僅為8x8mm,成本低,適合集成度高的網(wǎng)關(guān)模組等場(chǎng)景,軟件使用方法一致。更多信息和資料請(qǐng)進(jìn)入http://www.w5500.com/網(wǎng)站或者私信獲取。
此外,本W(wǎng)55MH32支持硬件加密算法單元,WIZnet還推出TOE+SSL應(yīng)用,涵蓋TCP SSL、HTTP SSL以及 MQTT SSL等,為網(wǎng)絡(luò)通信安全再添保障。
為助力開(kāi)發(fā)者快速上手與深入開(kāi)發(fā),基于W55MH32L這顆芯片,WIZnet精心打造了配套開(kāi)發(fā)板。開(kāi)發(fā)板集成WIZ-Link芯片,借助一根USB C口數(shù)據(jù)線,就能輕松實(shí)現(xiàn)調(diào)試、下載以及串口打印日志等功能。開(kāi)發(fā)板將所有外設(shè)全部引出,拓展功能也大幅提升,便于開(kāi)發(fā)者全面評(píng)估芯片性能。
若您想獲取芯片和開(kāi)發(fā)板的更多詳細(xì)信息,包括產(chǎn)品特性、技術(shù)參數(shù)以及價(jià)格等,歡迎訪問(wèn)官方網(wǎng)頁(yè):http://www.w5500.com/,我們期待與您共同探索W55MH32的無(wú)限可能。

第十八章 W55MH32 FTP_Server示例
本篇文章,我們將詳細(xì)介紹如何在W55MH32芯片上實(shí)現(xiàn)FTP協(xié)議。并通過(guò)實(shí)戰(zhàn)例程,為大家講解使用W55MH32作為FTP服務(wù)器、PC端作為FTP客戶端進(jìn)行文件傳輸、目錄操作等多種功能。
該例程用到的其他網(wǎng)絡(luò)協(xié)議,例如DHCP請(qǐng)參考相關(guān)章節(jié)。有關(guān)W55MH32的初始化過(guò)程,請(qǐng)參考Network Install章節(jié),這里將不再贅述。
1 FTP協(xié)議簡(jiǎn)介
FTP(File Transfer Protocol,文件傳輸協(xié)議)是一種標(biāo)準(zhǔn)的網(wǎng)絡(luò)協(xié)議,用于在客戶端和服務(wù)器之間傳輸文件。FTP客戶端協(xié)議是基于 FTP協(xié)議實(shí)現(xiàn)的,用來(lái)指導(dǎo)客戶端如何與 FTP服務(wù)器通信,實(shí)現(xiàn)文件的上傳、下載、目錄操作等功能。由于 FTP最初是以明文傳輸設(shè)計(jì)的,不夠安全,在 FTP上加入 SSL/TLS加密層,可提供加密的控制連接和數(shù)據(jù)連接。以下是 FTP客戶端協(xié)議的主要內(nèi)容和工作機(jī)制的介紹。
2 FTP協(xié)議特點(diǎn)
基于 TCP傳輸:FTP使用兩個(gè) TCP連接:控制連接(端口 21)和數(shù)據(jù)連接(端口 20或 PASV模式動(dòng)態(tài)分配的端口),確??煽康臄?shù)據(jù)傳輸。
分離控制與數(shù)據(jù):
控制連接用于發(fā)送命令和接收響應(yīng)。
數(shù)據(jù)連接用于文件內(nèi)容或目錄信息的傳輸。
支持多種傳輸模式:
主動(dòng)模式(Active Mode):服務(wù)器主動(dòng)連接客戶端的數(shù)據(jù)端口。
被動(dòng)模式(Passive Mode):客戶端主動(dòng)連接服務(wù)器提供的數(shù)據(jù)端口,解決 NAT防火墻限制。
支持多種文件操作:
文件上傳(STOR)、下載(RETR)、刪除(DELE)。
目錄操作(MKD、RMD、CWD、PWD)。
獲取文件列表(LIST、NLST)。
明文傳輸(傳統(tǒng) FTP):用戶名、密碼及數(shù)據(jù)以明文形式傳輸,不安全。安全改進(jìn):FTPS(FTP Secure,基于 SSL/TLS)和 SFTP(Secure File Transfer Protocol,基于 SSH)。
靈活的用戶認(rèn)證機(jī)制:
支持匿名登錄(匿名用戶可通過(guò) email作為密碼)。
支持認(rèn)證用戶名和密碼。
3 FTP協(xié)議應(yīng)用場(chǎng)景
接下來(lái),我們了解下在W55MH32上,可以使用FTP協(xié)議完成哪些操作及應(yīng)用呢?
固件升級(jí):嵌入式設(shè)備通過(guò) FTP下載新固件或軟件更新包進(jìn)行系統(tǒng)升級(jí)。適用于需要定期更新功能的設(shè)備,如路由器、工業(yè)控制設(shè)備等。
數(shù)據(jù)采集與傳輸:嵌入式設(shè)備(如傳感器節(jié)點(diǎn)或數(shù)據(jù)記錄器)將采集的數(shù)據(jù)上傳至遠(yuǎn)程服務(wù)器進(jìn)行存儲(chǔ)和分析。例如:環(huán)境監(jiān)測(cè)設(shè)備將溫濕度數(shù)據(jù)上傳到服務(wù)器。
遠(yuǎn)程配置與日志管理:設(shè)備通過(guò) FTP下載配置文件或上傳日志信息供管理員分析和排錯(cuò)。適用于工業(yè)自動(dòng)化設(shè)備和嵌入式監(jiān)控系統(tǒng)。
嵌入式 Web服務(wù)器的文件管理:許多嵌入式設(shè)備內(nèi)置簡(jiǎn)易 Web服務(wù)器,用于文件存儲(chǔ)或內(nèi)容分發(fā),通過(guò) FTP管理這些文件資源。
4 FTP協(xié)議基本工作流程
1.建立控制連接
客戶端初始化:客戶端啟動(dòng) FTP客戶端程序,指定要連接的 FTP服務(wù)器的地址和端口號(hào)(端口號(hào)為 21)。
TCP連接建立:客戶端通過(guò) TCP協(xié)議向服務(wù)器的 21端口發(fā)起連接請(qǐng)求。服務(wù)器監(jiān)聽(tīng)該端口,接收到請(qǐng)求后,與客戶端建立起一條 TCP連接,這個(gè)連接被稱為控制連接,主要用于傳輸 FTP命令和服務(wù)器的響應(yīng)信息。
身份驗(yàn)證:連接建立后,服務(wù)器會(huì)提示客戶端輸入用戶名和密碼進(jìn)行身份驗(yàn)證。客戶端發(fā)送相應(yīng)的用戶名和密碼信息到服務(wù)器,服務(wù)器驗(yàn)證通過(guò)后,才允許客戶端進(jìn)行后續(xù)操作。也有一些匿名 FTP服務(wù)器,允許用戶以“anonymous”作為用戶名,以電子郵件地址作為密碼進(jìn)行登錄,提供公開(kāi)的文件訪問(wèn)服務(wù)。
2.傳輸模式選擇
客戶端和服務(wù)器在控制連接上協(xié)商數(shù)據(jù)傳輸模式,主要有兩種模式:
主動(dòng)模式(PORT模式):客戶端通過(guò)控制連接告訴服務(wù)器自己的數(shù)據(jù)端口(客戶端隨機(jī)開(kāi)放的一個(gè)端口),服務(wù)器使用 20端口主動(dòng)連接客戶端的數(shù)據(jù)端口來(lái)傳輸數(shù)據(jù)。
被動(dòng)模式(PASV模式):客戶端發(fā)送 PASV命令給服務(wù)器,服務(wù)器在控制連接上告知客戶端自己開(kāi)放的一個(gè)臨時(shí)數(shù)據(jù)端口(通常是 1024以上的端口),然后客戶端使用自己的一個(gè)隨機(jī)端口連接服務(wù)器的這個(gè)臨時(shí)數(shù)據(jù)端口來(lái)傳輸數(shù)據(jù)。
3.數(shù)據(jù)傳輸
根據(jù)用戶的操作需求,通過(guò)數(shù)據(jù)連接進(jìn)行文件或目錄相關(guān)操作:
上傳文件:客戶端向服務(wù)器發(fā)送 STOR(存儲(chǔ))命令,然后通過(guò)數(shù)據(jù)連接將本地文件數(shù)據(jù)發(fā)送到服務(wù)器。服務(wù)器接收到數(shù)據(jù)后,將其存儲(chǔ)在指定的目錄下。
下載文件:客戶端向服務(wù)器發(fā)送 RETR(檢索)命令,請(qǐng)求下載服務(wù)器上的文件。服務(wù)器通過(guò)數(shù)據(jù)連接將文件數(shù)據(jù)發(fā)送給客戶端,客戶端接收數(shù)據(jù)并將其保存到本地指定位置。
目錄操作:客戶端還可以發(fā)送諸如 LIST(列出目錄內(nèi)容)、CWD(更改工作目錄)、MKD(創(chuàng)建目錄)、RMD(刪除目錄)等命令,服務(wù)器執(zhí)行相應(yīng)操作,并通過(guò)控制連接返回操作結(jié)果。執(zhí)行這些命令時(shí),若需要傳輸目錄列表等數(shù)據(jù),也會(huì)通過(guò)數(shù)據(jù)連接進(jìn)行傳輸。
4.關(guān)閉連接
數(shù)據(jù)連接關(guān)閉:在完成文件傳輸或其他操作后,數(shù)據(jù)連接會(huì)被關(guān)閉。如果還有其他操作需要進(jìn)行,客戶端和服務(wù)器可以根據(jù)需要重新建立數(shù)據(jù)連接。
控制連接關(guān)閉:當(dāng)客戶端完成所有操作后,會(huì)向服務(wù)器發(fā)送 QUIT命令,服務(wù)器接收到該命令后,會(huì)關(guān)閉控制連接。至此,客戶端與服務(wù)器之間的 FTP會(huì)話結(jié)束。
5主動(dòng)模式與被動(dòng)模式詳解
主動(dòng)模式(Active Mode):
客戶端打開(kāi)一個(gè)端口并監(jiān)聽(tīng)。
客戶端通過(guò)控制連接告訴服務(wù)器自己的 IP和端口。
服務(wù)器主動(dòng)連接到客戶端指定的端口傳輸數(shù)據(jù)。
被動(dòng)模式(Passive Mode):
客戶端通過(guò)控制連接請(qǐng)求被動(dòng)模式。
服務(wù)器打開(kāi)一個(gè)隨機(jī)端口并通過(guò)控制連接告知客戶端。
客戶端主動(dòng)連接到服務(wù)器指定的端口傳輸數(shù)據(jù)。
優(yōu)缺點(diǎn)對(duì)比:
主動(dòng)模式更適合在服務(wù)器端網(wǎng)絡(luò)無(wú)防火墻限制的環(huán)境。
被動(dòng)模式更適合客戶端在 NAT或防火墻后的情況。
6 FTP報(bào)文解析
FTP 報(bào)文分為命令和響應(yīng)報(bào)文,命令報(bào)文用于發(fā)送操作請(qǐng)求,響應(yīng)報(bào)文用于返回結(jié)果。
命令報(bào)文格式為“<命令> <參數(shù)>rn”,字段解釋如下:
<命令>:FTP命令(如 USER、PASS)。
<參數(shù)>:命令的附加信息(如用戶名、文件名)。
例如“USER usernamern”。常見(jiàn)的命令包括登錄 (USER, PASS)、文件操作 (RETR, STOR)、目錄操作 (LIST, CWD) 等。每個(gè) FTP報(bào)文由命令或響應(yīng)代碼、狀態(tài)碼及附加數(shù)據(jù)組成,狀態(tài)碼用于指示操作結(jié)果。
以下是 FTP常見(jiàn)命令:
USER:提供用戶名進(jìn)行身份驗(yàn)證。
PASS:提供密碼進(jìn)行身份驗(yàn)證。
CWD:更改當(dāng)前工作目錄。
PWD:顯示當(dāng)前工作目錄。
LIST:列出目錄下的文件和子目錄。
RETR:從服務(wù)器下載文件。
STOR:上傳文件到服務(wù)器。
DELE:刪除指定文件。
MKD:創(chuàng)建新目錄。
RMD:刪除目錄。
QUIT:終止會(huì)話并退出。
TYPE:設(shè)置文件傳輸類型(ASCII或 Binary)。
PORT:指定數(shù)據(jù)連接的端口。
PASV:啟用被動(dòng)模式,服務(wù)器指定端口供客戶端連接。
響應(yīng)報(bào)文格式為“<狀態(tài)碼> <說(shuō)明文字>rn”,字段解釋如下:
<狀態(tài)碼>:三位數(shù)字表示狀態(tài)。
<說(shuō)明文字>:狀態(tài)的文字描述。
例如“230 User logged in, proceed.rn”。以下是FTP常見(jiàn)的響應(yīng)碼:
1xx(信息性響應(yīng)):主要是提供一些初步的信息,通常表示服務(wù)器正在處理請(qǐng)求,還沒(méi)有完成操作。
2xx(成功響應(yīng)):表示命令成功執(zhí)行。這是客戶端最希望看到的響應(yīng)類型之一,說(shuō)明請(qǐng)求的操作(如登錄、文件傳輸?shù)龋╉樌瓿伞?/p>
3xx(補(bǔ)充信息響應(yīng)):表示服務(wù)器需要一些額外的信息才能完成操作。通常是在身份驗(yàn)證或者文件定位等過(guò)程中出現(xiàn)。
4xx(暫時(shí)錯(cuò)誤響應(yīng)):表示客戶端的請(qǐng)求有問(wèn)題,但錯(cuò)誤是暫時(shí)的,可能通過(guò)一些調(diào)整(如重新發(fā)送請(qǐng)求等)可以解決。
5xx(永久性錯(cuò)誤響應(yīng)):表示客戶端的請(qǐng)求存在錯(cuò)誤,并且這個(gè)錯(cuò)誤是比較嚴(yán)重的,很難通過(guò)簡(jiǎn)單的調(diào)整來(lái)糾正。
接著我們來(lái)看看FTP獲取目錄的報(bào)文示例:
客戶端建立TCP連接到服務(wù)器的21端口
服務(wù)器返回:220 Welcome to FTP Serverrn
客戶端發(fā)送:USER wiznetrn
服務(wù)器返回:331 User wiznet OK.Password requiredrn
客戶端發(fā)送:PASS wiznetrn
服務(wù)器返回:230 User logged inrn
客戶端發(fā)送PORT 192,168,1,5,20,100rn(主動(dòng)模式,192,168,1,5是客戶端的地址,20,100是客戶端期望的端口號(hào)20*256+100=5260)
服務(wù)器返回:200 PORT command successfulrn
客戶端發(fā)送:LISTrn(DIR命令,獲取當(dāng)前目錄的文件信息)
服務(wù)器回復(fù):150 Opening ASCII mode data connection for file listrn
服務(wù)器像客戶端期望的端口號(hào)發(fā)起TCP連接,并傳輸目錄信息,傳輸完成后關(guān)閉TCP連接。
客戶端發(fā)送:QUITrn(退出FTP會(huì)話)
服務(wù)器回復(fù):221 Goodbyern
7實(shí)現(xiàn)過(guò)程
接下來(lái),我們看看如何在W55MH32上實(shí)現(xiàn)FTP協(xié)議的Server模式。
注意:測(cè)試實(shí)例需要PC端和W55MH32處于同一網(wǎng)段。
步驟一:獲取網(wǎng)絡(luò)配置信息和FTP初始化
wizchip_getnetinfo(&net_info); ftpd_init(net_info.ip);
ftpd_init()函數(shù)內(nèi)容如下:
/** * @brief Initialize the FTP server * * Initialize the FTP server, set the status, commands, modes and other parameters of the FTP server, and initialize the FTP login username and password. * * @param src_ip: Source IP address */ void ftpd_init(uint8_t *src_ip) { ftp.state = FTPS_NOT_LOGIN; ftp.current_cmd = NO_CMD; ftp.dsock_mode = ACTIVE_MODE; ftp.ID_Enable = STATUS_USED; ftp.PW_Enable = STATUS_USED; if (ftp.ID_Enable == STATUS_USED) { strcpy(ftp.username, ftp_ID); printf(" FTP ID[%d]:%s rn", strlen(ftp.username), ftp.username); } if (ftp.PW_Enable == STATUS_USED) { strcpy(ftp.userpassword, ftp_PW); printf(" FTP PW[%d]:%s rn", strlen(ftp.userpassword), ftp.userpassword); } 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; strcpy(ftp.workingdir, "/"); socket(CTRL_SOCK, Sn_MR_TCP, IPPORT_FTP, 0x0); socket(CTRL_SOCK1, Sn_MR_TCP, IPPORT_FTP, 0x0); }
ftpd_init()函數(shù)的主要作用是對(duì) FTP服務(wù)器的各種參數(shù)進(jìn)行初始化設(shè)置,包括服務(wù)器狀態(tài)、用戶認(rèn)證信息、網(wǎng)絡(luò)地址和端口,以及創(chuàng)建TCP socket,為后續(xù)的 FTP服務(wù)運(yùn)行做好準(zhǔn)備。
步驟二:實(shí)現(xiàn)服務(wù)器和客戶端之間的持續(xù)交互
ftpd_run()函數(shù)在主循環(huán)中不斷被調(diào)用,作用是讓 FTP服務(wù)器持續(xù)運(yùn)行,不斷處理客戶端的各種請(qǐng)求,實(shí)現(xiàn)服務(wù)器與客戶端之間的持續(xù)交互,以提供穩(wěn)定的 FTP服務(wù) 。
while (1)
{
ftpd_run(ethernet_buf);
}
ftpd_run()函數(shù)內(nèi)容如下:
c
運(yùn)行
uint8_t ftpd_run(uint8_t *dbuf)
{
uint16_t size = 0;
long ret = 0;
uint32_t blocklen, recv_byte;
uint32_t remain_filesize;
int32_t remain_datasize;
#if defined(F_FILESYSTEM)
FILINFO fno;
#endif
// FTP Control 1
#if 1
switch (getSn_SR(CTRL_SOCK))
{
case SOCK_ESTABLISHED:
if (!connect_state_control)
{
#if defined(_FTP_DEBUG_)
printf("%d:FTP Connectedrn", CTRL_SOCK);
#endif
// fsprintf(CTRL_SOCK, banner, HOSTNAME, VERSION);
strcpy(ftp.workingdir, "/");
sprintf((char *)dbuf, "220 %s FTP version %s ready.rn", HOSTNAME, VERSION);
ret = send(CTRL_SOCK, (uint8_t *)dbuf, strlen((const char *)dbuf));
#if defined(_FTP_DEBUG_)
printf("%d:send() [%s]rn", CTRL_SOCK, dbuf);
#endif
if (ret < 0)
{
#if defined(_FTP_DEBUG_)
printf("%d:send() error:%ldrn", CTRL_SOCK, ret);
#endif
close(CTRL_SOCK);
return ret;
}
connect_state_control = 1;
}
#if connect_timeout_en
else
{
if (con_remain_cnt1 > remain_time)
{
if ((ret = disconnect(CTRL_SOCK)) != SOCK_OK)
return ret;
#if defined(_FTP_DEBUG_)
printf("%d:Timeout Closedrn", CTRL_SOCK);
#endif
}
#if defined(_FTP_DEBUG_)
else if (((con_remain_cnt1 % 10000) == 0) && (con_remain_cnt1 != 0))
{
// printf("%d:Timeout Count:%ldrn", CTRL_SOCK, con_remain_cnt1);
}
#endif
con_remain_cnt1++;
}
#endif
#if defined(_FTP_DEBUG_)
// printf("ftp socket %drn", CTRL_SOCK);
#endif
if ((size = getSn_RX_RSR(CTRL_SOCK)) > 0) // Don't need to check SOCKERR_BUSY because it doesn't not occur.
{
#if defined(_FTP_DEBUG_)
printf("%d:size: %drn", CTRL_SOCK, size);
#endif
memset(dbuf, 0, _MAX_SS);
if (size > _MAX_SS)
size = _MAX_SS - 1;
ret = recv(CTRL_SOCK, dbuf, size);
dbuf[ret] = '';
if (ret != size)
{
if (ret == SOCK_BUSY)
return 0;
if (ret < 0)
{
#if defined(_FTP_DEBUG_)
printf("%d:recv() error:%ldrn", CTRL_SOCK, ret);
#endif
close(CTRL_SOCK);
return ret;
}
}
#if defined(_FTP_DEBUG_)
printf("%d:Rcvd Command: %s", CTRL_SOCK, dbuf);
#endif
proc_ftpd(CTRL_SOCK, (char *)dbuf);
con_remain_cnt1 = 0;
}
break;
case SOCK_CLOSE_WAIT:
#if defined(_FTP_DEBUG_)
printf("%d:CloseWaitrn", CTRL_SOCK);
#endif
if ((ret = disconnect(CTRL_SOCK)) != SOCK_OK)
return ret;
#if defined(_FTP_DEBUG_)
printf("%d:Closedrn", CTRL_SOCK);
#endif
break;
case SOCK_CLOSED:
#if defined(_FTP_DEBUG_)
printf("%d:FTPStartrn", CTRL_SOCK);
#endif
if ((ret = socket(CTRL_SOCK, Sn_MR_TCP, IPPORT_FTP, 0x0)) != CTRL_SOCK)
{
#if defined(_FTP_DEBUG_)
printf("%d:socket() error:%ldrn", CTRL_SOCK, ret);
#endif
close(CTRL_SOCK);
return ret;
}
break;
case SOCK_INIT:
#if defined(_FTP_DEBUG_)
printf("%d:Openedrn", CTRL_SOCK);
#endif
strcpy(ftp.workingdir, "/");
if ((ret = listen(CTRL_SOCK)) != SOCK_OK)
{
#if defined(_FTP_DEBUG_)
printf("%d:Listen errorrn", CTRL_SOCK);
#endif
return ret;
}
connect_state_control = 0;
con_remain_cnt1 = 0;
#if defined(_FTP_DEBUG_)
printf("%d:Listen okrn", CTRL_SOCK);
#endif
break;
default:
break;
}
#endif
進(jìn)入ftpd_run()函數(shù)后,程序會(huì)執(zhí)行一個(gè)TCP Server模式的狀態(tài)機(jī)(具體可參考TCP Server章節(jié)),當(dāng)socket處于SOCK_ESTABLISHED狀態(tài)時(shí),如果是首次進(jìn)入SOCK_ESTABLISHED狀態(tài),則會(huì)向客戶端發(fā)送歡迎消息。后續(xù)則是監(jiān)聽(tīng)客戶端指令,當(dāng)收到客戶端指令后,會(huì)進(jìn)入proc_ftpd()進(jìn)行處理。
proc_ftpd()函數(shù)處理 FTP服務(wù)的命令,根據(jù)不同命令及參數(shù)進(jìn)行相應(yīng)操作,包括用戶認(rèn)證、文件操作、數(shù)據(jù)傳輸、狀態(tài)處理及錯(cuò)誤響應(yīng)。
注意:當(dāng)宏定義connect_timeout_en的值設(shè)置為 1時(shí),會(huì)啟用連接超時(shí)功能。如果在超過(guò)宏定義remain_time所設(shè)定的時(shí)長(zhǎng)后,仍然沒(méi)有進(jìn)行任何操作,系統(tǒng)將自動(dòng)斷開(kāi)連接。其中,connect_timeout_en是一個(gè)控制連接超時(shí)功能開(kāi)啟或關(guān)閉的宏,其值為 1表示開(kāi)啟該功能,為 0表示關(guān)閉;而remain_time是一個(gè)宏,它定義了在觸發(fā)自動(dòng)斷開(kāi)連接操作前的最大允許無(wú)操作時(shí)長(zhǎng)。
proc_ftpd()函數(shù)如下:
/**
* Processes FTP-related data or request.
*
* @param sn Identifier for distinguishing requests/sessions.
* @param buf Buffer to store result or status information.
*
* @return Character indicating operation status ('S' for success, 'E' for error, etc.).
*
* Note: Refer to documentation for exact behavior and meaning of return value.
*/
char proc_ftpd(uint8_t sn, char *buf)
{
char **cmdp, *cp, *arg, *tmpstr;
char sendbuf[200];
int slen;
long ret;
// Translate first word to lower case
for (cp = buf; *cp != ' ' && *cp != ''; cp++)
*cp = tolower(*cp);
// Find command in table; if not present, return syntax error
for (cmdp = commands; *cmdp != NULL; cmdp++)
if (strncmp(*cmdp, buf, strlen(*cmdp)) == 0)
break;
if (*cmdp == NULL)
{
// fsprintf(CTRL_SOCK, badcmd, buf);
slen = sprintf(sendbuf, "500 Unknown command '%s'rn", buf);
send(sn, (uint8_t *)sendbuf, slen);
return 0;
}
// Allow only USER, PASS and QUIT before logging in
if (ftp.state == FTPS_NOT_LOGIN)
{
switch (cmdp - commands)
{
case USER_CMD:
case PASS_CMD:
case QUIT_CMD:
break;
default:
// fsprintf(CTRL_SOCK, notlog);
slen = sprintf(sendbuf, "530 Please log in with USER and PASSrn");
send(sn, (uint8_t *)sendbuf, slen);
return 0;
}
}
arg = &buf[strlen(*cmdp)];
while (*arg == ' ')
arg++;
/* Execute specific command */
switch (cmdp - commands)
{
case USER_CMD:
#if defined(_FTP_DEBUG_)
printf("USER_CMD : %s", arg);
#endif
slen = strlen(arg);
arg[slen - 1] = 0x00;
arg[slen - 2] = 0x00;
if (ftp.ID_Enable == STATUS_USED)
{
if (strcmp(ftp.username, arg) != 0)
{
slen = sprintf(sendbuf, "430 Invalid usernamern");
ret = send(sn, (uint8_t *)sendbuf, slen);
if (ret < 0)
{
#if defined(_FTP_DEBUG_)
printf("%d:send() error:%ldrn", sn, ret);
#endif
close(sn);
return ret;
}
break;
}
}
else
{
strcpy(ftp.username, arg);
}
// fsprintf(CTRL_SOCK, givepass);
slen = sprintf(sendbuf, "331 Enter PASS commandrn");
ret = send(sn, (uint8_t *)sendbuf, slen);
if (ret < 0)
{
#if defined(_FTP_DEBUG_)
printf("%d:send() error:%ldrn", sn, ret);
#endif
close(sn);
return ret;
}
break;
case PASS_CMD:
#if defined(_FTP_DEBUG_)
printf("PASS_CMD : %s", arg);
#endif
slen = strlen(arg);
arg[slen - 1] = 0x00;
arg[slen - 2] = 0x00;
if (ftp.PW_Enable == STATUS_USED)
{
if (strcmp(ftp.userpassword, arg) != 0)
{
slen = sprintf(sendbuf, "430 Invalid passwordrn");
ret = send(sn, (uint8_t *)sendbuf, slen);
if (ret < 0)
{
#if defined(_FTP_DEBUG_)
printf("%d:send() error:%ldrn", sn, ret);
#endif
close(sn);
return ret;
}
break;
}
}
ftplogin(sn, arg);
break;
case TYPE_CMD:
slen = strlen(arg);
arg[slen - 1] = 0x00;
arg[slen - 2] = 0x00;
switch (arg[0])
{
case 'A':
case 'a': // Ascii
ftp.type = ASCII_TYPE;
// fsprintf(CTRL_SOCK, typeok, arg);
slen = sprintf(sendbuf, "200 Type set to %srn", arg);
send(sn, (uint8_t *)sendbuf, slen);
break;
case 'B':
case 'b': // Binary
case 'I':
case 'i': // Image
ftp.type = IMAGE_TYPE;
// fsprintf(CTRL_SOCK, typeok, arg);
slen = sprintf(sendbuf, "200 Type set to %srn", arg);
send(sn, (uint8_t *)sendbuf, slen);
break;
default: /* Invalid */
// fsprintf(CTRL_SOCK, badtype, arg);
slen = sprintf(sendbuf, "501 Unknown type "%s"rn", arg);
send(sn, (uint8_t *)sendbuf, slen);
break;
}
break;
case FEAT_CMD:
slen = sprintf(sendbuf, "211-Features:rn MDTMrn REST STREAMrn SIZErn MLST size*;type*;create*;modify*;rn MLSDrn UTF8rn CLNTrn MFMTrn211 ENDrn");
send(sn, (uint8_t *)sendbuf, slen);
break;
case QUIT_CMD:
#if defined(_FTP_DEBUG_)
printf("QUIT_CMDrn");
#endif
// fsprintf(CTRL_SOCK, bye);
slen = sprintf(sendbuf, "221 Goodbye!rn");
send(sn, (uint8_t *)sendbuf, slen);
disconnect(sn);
break;
case RETR_CMD:
slen = strlen(arg);
arg[slen - 1] = 0x00;
arg[slen - 2] = 0x00;
#if defined(_FTP_DEBUG_)
printf("RETR_CMDrn");
#endif
if (strlen(ftp.workingdir) == 1)
sprintf(ftp.filename, "/%s", arg);
else
sprintf(ftp.filename, "%s/%s", ftp.workingdir, arg);
slen = sprintf(sendbuf, "150 Opening data channel for file downloand from server of "%s"rn", ftp.filename);
send(sn, (uint8_t *)sendbuf, slen);
ftp.current_cmd = RETR_CMD;
break;
case APPE_CMD:
case STOR_CMD:
slen = strlen(arg);
arg[slen - 1] = 0x00;
arg[slen - 2] = 0x00;
#if defined(_FTP_DEBUG_)
printf("STOR_CMDrn");
#endif
if (strlen(ftp.workingdir) == 1)
sprintf(ftp.filename, "/%s", arg);
else
sprintf(ftp.filename, "%s/%s", ftp.workingdir, arg);
slen = sprintf(sendbuf, "150 Opening data channel for file upload to server of "%s"rn", ftp.filename);
send(sn, (uint8_t *)sendbuf, slen);
ftp.current_cmd = STOR_CMD;
if (ftp.dsock_mode == ACTIVE_MODE)
{
if ((ret = connect(DATA_SOCK, remote_ip.cVal, remote_port)) != SOCK_OK)
{
#if defined(_FTP_DEBUG_)
printf("%d:Connect errorrn", DATA_SOCK);
#endif
return ret;
}
}
connect_state_data = 0;
break;
case PORT_CMD:
#if defined(_FTP_DEBUG_)
printf("PORT_CMDrn");
#endif
if (pport(arg) == -1)
{
// fsprintf(CTRL_SOCK, badport);
slen = sprintf(sendbuf, "501 Bad port syntaxrn");
send(sn, (uint8_t *)sendbuf, slen);
}
else
{
// fsprintf(CTRL_SOCK, portok);
ftp.dsock_mode = ACTIVE_MODE;
ftp.dsock_state = DATASOCK_READY;
slen = sprintf(sendbuf, "200 PORT command successful.rn");
send(sn, (uint8_t *)sendbuf, slen);
}
break;
case MLSD_CMD:
#if defined(_FTP_DEBUG_)
printf("MLSD_CMDrn");
#endif
slen = sprintf(sendbuf, "150 Opening data channel for directory listing of "%s"rn", ftp.workingdir);
send(sn, (uint8_t *)sendbuf, slen);
ftp.current_cmd = MLSD_CMD;
break;
case LIST_CMD:
#if defined(_FTP_DEBUG_)
printf("LIST_CMDrn");
#endif
slen = sprintf(sendbuf, "150 Opening data channel for directory listing of "%s"rn", ftp.workingdir);
send(sn, (uint8_t *)sendbuf, slen);
ftp.current_cmd = LIST_CMD;
break;
case NLST_CMD:
#if defined(_FTP_DEBUG_)
printf("NLST_CMDrn");
#endif
break;
case SYST_CMD:
slen = sprintf(sendbuf, "215 UNIX emulated by WIZnetrn");
send(sn, (uint8_t *)sendbuf, slen);
break;
case PWD_CMD:
case XPWD_CMD:
slen = sprintf(sendbuf, "257 "%s" is current directory.rn", ftp.workingdir);
send(sn, (uint8_t *)sendbuf, slen);
break;
case PASV_CMD:
slen = sprintf(sendbuf, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)rn", local_ip.cVal[0], local_ip.cVal[1], local_ip.cVal[2], local_ip.cVal[3], local_port >> 8, local_port & 0x00ff);
send(sn, (uint8_t *)sendbuf, slen);
if (getSn_SR(DATA_SOCK) == SOCK_ESTABLISHED)
{
#if defined(_FTP_DEBUG_)
printf("data disconnect: %drn", DATA_SOCK);
#endif
disconnect(DATA_SOCK);
}
ftp.dsock_mode = PASSIVE_MODE;
ftp.dsock_state = DATASOCK_READY;
cur_sn = sn;
#if defined(_FTP_DEBUG_)
printf("PASV port: %drn", local_port);
#endif
break;
case SIZE_CMD:
slen = strlen(arg);
arg[slen - 1] = 0x00;
arg[slen - 2] = 0x00;
if (slen > 3)
{
tmpstr = strrchr(arg, '/');
*tmpstr = 0;
#if defined(F_FILESYSTEM)
slen = get_filesize(arg, tmpstr + 1);
#else
slen = _MAX_SS;
#endif
if (slen > 0)
slen = sprintf(sendbuf, "213 %drn", slen);
else
slen = sprintf(sendbuf, "550 File not Foundrn");
}
else
{
slen = sprintf(sendbuf, "550 File not Foundrn");
}
send(sn, (uint8_t *)sendbuf, slen);
break;
case CWD_CMD:
slen = strlen(arg);
arg[slen - 1] = 0x00;
arg[slen - 2] = 0x00;
if (slen > 3)
{
arg[slen - 3] = 0x00;
tmpstr = strrchr(arg, '/');
*tmpstr = 0;
#if defined(F_FILESYSTEM)
slen = get_filesize(arg, tmpstr + 1);
#else
slen = 0;
#endif
*tmpstr = '/';
if (slen == 0)
{
slen = sprintf(sendbuf, "213 %drn", slen);
strcpy(ftp.workingdir, arg);
slen = sprintf(sendbuf, "250 CWD successful. "%s" is current directory.rn", ftp.workingdir);
}
else
{
slen = sprintf(sendbuf, "550 CWD failed. "%s"rn", arg);
}
}
else
{
strcpy(ftp.workingdir, arg);
slen = sprintf(sendbuf, "250 CWD successful. "%s" is current directory.rn", ftp.workingdir);
}
send(sn, (uint8_t *)sendbuf, slen);
break;
case MKD_CMD:
case XMKD_CMD:
slen = strlen(arg);
arg[slen - 1] = 0x00;
arg[slen - 2] = 0x00;
#if defined(F_FILESYSTEM)
if (f_mkdir(arg) != 0)
{
slen = sprintf(sendbuf, "550 Can't create directory. "%s"rn", arg);
}
else
{
slen = sprintf(sendbuf, "257 MKD command successful. "%s"rn", arg);
// strcpy(ftp.workingdir, arg);
}
#else
slen = sprintf(sendbuf, "550 Can't create directory. Permission deniedrn");
#endif
send(sn, (uint8_t *)sendbuf, slen);
break;
case DELE_CMD:
slen = strlen(arg);
arg[slen - 1] = 0x00;
arg[slen - 2] = 0x00;
#if defined(F_FILESYSTEM)
if (f_unlink(arg) != 0)
{
slen = sprintf(sendbuf, "550 Could not delete. "%s"rn", arg);
}
else
{
slen = sprintf(sendbuf, "250 Deleted. "%s"rn", arg);
}
#else
slen = sprintf(sendbuf, "550 Could not delete. Permission deniedrn");
#endif
send(sn, (uint8_t *)sendbuf, slen);
break;
case XCWD_CMD:
case ACCT_CMD:
case XRMD_CMD:
case RMD_CMD:
case STRU_CMD:
case MODE_CMD:
case XMD5_CMD:
// fsprintf(CTRL_SOCK, unimp);
slen = sprintf(sendbuf, "502 Command does not implemented yet.rn");
send(sn, (uint8_t *)sendbuf, slen);
break;
default: // Invalid
// fsprintf(CTRL_SOCK, badcmd, arg);
slen = sprintf(sendbuf, "500 Unknown command '%s'rn", arg);
send(sn, (uint8_t *)sendbuf, slen);
break;
}
return 1;
}
進(jìn)入 proc_ftpd()函數(shù)后,程序會(huì)執(zhí)行一個(gè)狀態(tài)機(jī),首先將接收的命令轉(zhuǎn)換為小寫(xiě)并在命令表中查找,未找到時(shí)發(fā)送錯(cuò)誤信息。登錄前僅允許 USER、PASS、QUIT命令,其余報(bào)錯(cuò)。對(duì)于不同命令,如 USER_CMD處理用戶名驗(yàn)證和后續(xù)操作,PASS_CMD進(jìn)行密碼驗(yàn)證和登錄,TYPE_CMD處理傳輸類型設(shè)置,F(xiàn)EAT_CMD發(fā)送特性信息,QUIT_CMD斷開(kāi)連接,還有 RETR_CMD等文件操作命令,以及 PORT_CMD、PASV_CMD等數(shù)據(jù)連接模式相關(guān)命令,根據(jù)不同情況執(zhí)行相應(yīng)的操作和錯(cuò)誤處理,同時(shí)發(fā)送相應(yīng)的狀態(tài)信息。
8運(yùn)行結(jié)果
燒錄例程運(yùn)行后,首先進(jìn)行了PHY鏈路檢測(cè),然后打印設(shè)置網(wǎng)絡(luò)信息。打開(kāi)filezilla軟件(下載鏈接:客戶端 - FileZilla中文網(wǎng)),在filezilla軟件上填寫(xiě)主機(jī)ID,用戶名,密碼,端口號(hào)(通常是21)連接FTP服務(wù)器。連接成功顯示如下界面:

然后拉住本地站點(diǎn)文件向遠(yuǎn)程站點(diǎn)(服務(wù)器)內(nèi)拖動(dòng),成功向服務(wù)器傳輸文件。

9總結(jié)
本文講解了如何在 W55MH32芯片上實(shí)現(xiàn) FTP協(xié)議的服務(wù)器模式,通過(guò)實(shí)戰(zhàn)例程展示了使用 W55MH32作為 FTP服務(wù)器與 PC端進(jìn)行文件傳輸、目錄操作等功能的過(guò)程,涵蓋獲取網(wǎng)絡(luò)配置信息和 FTP初始化、實(shí)現(xiàn)服務(wù)器和客戶端之間的持續(xù)交互等關(guān)鍵步驟。文章詳細(xì)介紹了 FTP協(xié)議的概念、特點(diǎn)、應(yīng)用場(chǎng)景、基本工作流程、主動(dòng)與被動(dòng)模式、報(bào)文解析,幫助讀者理解其在文件傳輸中的實(shí)際應(yīng)用價(jià)值。
下一篇文章將聚焦 FTP協(xié)議客戶端模式,解析其核心原理及在文件傳輸中的應(yīng)用,同時(shí)講解如何在W55MH32上實(shí)現(xiàn) FTP客戶端功能,敬請(qǐng)期待!
WIZnet是一家無(wú)晶圓廠半導(dǎo)體公司,成立于 1998年。產(chǎn)品包括互聯(lián)網(wǎng)處理器 iMCU?,它采用 TOE(TCP/IP卸載引擎)技術(shù),基于獨(dú)特的專利全硬連線 TCP/IP。iMCU?面向各種應(yīng)用中的嵌入式互聯(lián)網(wǎng)設(shè)備。
WIZnet在全球擁有 70多家分銷商,在香港、韓國(guó)、美國(guó)設(shè)有辦事處,提供技術(shù)支持和產(chǎn)品營(yíng)銷。
香港辦事處管理的區(qū)域包括:澳大利亞、印度、土耳其、亞洲(韓國(guó)和日本除外)。
審核編輯 黃宇
-
以太網(wǎng)
+關(guān)注
關(guān)注
41文章
5913瀏覽量
179426 -
數(shù)據(jù)傳輸
+關(guān)注
關(guān)注
9文章
2071瀏覽量
67159
發(fā)布評(píng)論請(qǐng)先 登錄
第十八章 I2C通信測(cè)試
第二章 W55MH32 DHCP示例
第五章 W55MH32 UDP示例
第九章 W55MH32 HTTP Server示例
第十章 W55MH32 SNTP示例
第十一章 W55MH32 SMTP示例
第十三章 W55MH32 UPnP端口轉(zhuǎn)發(fā)示例
第十四章 W55MH32 TFTP示例
第十五章 W55MH32 SNMP示例
第十六章 W55MH32 PING示例
第十七章 W55MH32 ARP示例
第十九章 W55MH32 FTP_Client示例
第二十六章 W55MH32?上位機(jī)搜索和配置示例
第二十九章 W55MH32 Modbus_TCP_Server示例
第三十章 W55MH32 HTTP_Server&NetBIOS示例

第十八章 W55MH32 FTP_Server示例
評(píng)論