單芯片解決方案,開(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 UPnP端口轉(zhuǎn)發(fā)示例
本篇文章,我們將詳細(xì)介紹如何在W55MH32芯片上面實(shí)現(xiàn)UPnP協(xié)議。使用W55MH32的TOE引擎,我們只需進(jìn)行簡(jiǎn)單的socket編程及寄存器讀寫(xiě),便可輕松實(shí)現(xiàn)以太網(wǎng)應(yīng)用。接下來(lái)我們通過(guò)實(shí)戰(zhàn)例程,為大家講解如何使用TOE引擎實(shí)現(xiàn)UPnP協(xié)議的端口轉(zhuǎn)發(fā)功能。
該例程用到的其他網(wǎng)絡(luò)協(xié)議,例如DHCP,請(qǐng)參考相關(guān)章節(jié)。有關(guān)W55MH32的初始化過(guò)程,請(qǐng)參考Network Install章節(jié),這里將不再贅述。
1 UPnP協(xié)議簡(jiǎn)介
UPnP(Universal Plug and Play)協(xié)議是一種支持設(shè)備在局域網(wǎng)中實(shí)現(xiàn)自動(dòng)發(fā)現(xiàn)和通信的網(wǎng)絡(luò)協(xié)議。其端口轉(zhuǎn)發(fā)功能由IGD Profile提供,允許局域網(wǎng)設(shè)備動(dòng)態(tài)請(qǐng)求路由器為其開(kāi)放指定的端口,以實(shí)現(xiàn)外部設(shè)備訪問(wèn)內(nèi)部服務(wù)。這種功能消除了手動(dòng)配置端口轉(zhuǎn)發(fā)的復(fù)雜性,特別適用于需要穿透NAT(網(wǎng)絡(luò)地址轉(zhuǎn)換)環(huán)境的應(yīng)用場(chǎng)景。
IGD(Internet Gateway Device,互聯(lián)網(wǎng)網(wǎng)關(guān)設(shè)備)是UPnP(Universal Plug and Play)協(xié)議的一部分,主要用于管理網(wǎng)絡(luò)中的網(wǎng)關(guān)設(shè)備(如路由器)的服務(wù)和資源。IGD擴(kuò)展定義了一套標(biāo)準(zhǔn)接口,允許局域網(wǎng)設(shè)備與網(wǎng)關(guān)設(shè)備通信,動(dòng)態(tài)配置網(wǎng)絡(luò)設(shè)置,例如端口轉(zhuǎn)發(fā)、帶寬管理和連接狀態(tài)查詢等。
2 UPnP協(xié)議特點(diǎn)
自動(dòng)化配置:無(wú)需用戶手動(dòng)設(shè)置,減少了配置錯(cuò)誤的風(fēng)險(xiǎn)。
動(dòng)態(tài)靈活:端口映射規(guī)則可以根據(jù)需求動(dòng)態(tài)添加或刪除。
設(shè)備友好:支持即插即用,簡(jiǎn)化了設(shè)備的聯(lián)網(wǎng)和部署過(guò)程。
跨設(shè)備兼容:UPnP基于標(biāo)準(zhǔn)化協(xié)議,廣泛支持各種設(shè)備和平臺(tái)。
3 UPnP應(yīng)用場(chǎng)景
通過(guò)UPnP端口轉(zhuǎn)發(fā)功能,我們可以使用W55MH32實(shí)現(xiàn)以下功能:
遠(yuǎn)程訪問(wèn):將外部請(qǐng)求轉(zhuǎn)發(fā)到局域網(wǎng)設(shè)備(如NAS、監(jiān)控?cái)z像頭),實(shí)現(xiàn)外部遠(yuǎn)程訪問(wèn)內(nèi)部設(shè)備。
遠(yuǎn)程控制:外部設(shè)備通過(guò)UPnP轉(zhuǎn)換的端口,可以實(shí)現(xiàn)遠(yuǎn)程控制局域網(wǎng)內(nèi)部設(shè)備(智能門(mén)鎖、燈光控制器)。
4 UPnP設(shè)置端口轉(zhuǎn)發(fā)的工作流程
設(shè)備發(fā)現(xiàn):W55MH32通過(guò)SSDP(Simple Service Discovery Protocol)向局域網(wǎng)中發(fā)送組播請(qǐng)求(HTTP M-SEARCH報(bào)文),搜索支持IGD的網(wǎng)關(guān)設(shè)備。
獲取服務(wù)描述:W55MH32訪問(wèn)網(wǎng)關(guān)設(shè)備(路由器)獲取服務(wù)描述文件,了解支持的服務(wù)和接口。
訂閱IGD事件:通過(guò)事件訂閱,W55MH32可以在不主動(dòng)輪詢的情況下,接收實(shí)時(shí)通知。
調(diào)用服務(wù)接口:使用UPnP的SOAT消息調(diào)用IGD提供的端口映射接口。
數(shù)據(jù)交互測(cè)試:外部通過(guò)訪問(wèn)映射的端口及路由器地址和局域網(wǎng)內(nèi)部設(shè)備進(jìn)行通信。
5報(bào)文講解
設(shè)備搜索
上文我們提到,設(shè)備搜索時(shí)使用SSDP協(xié)議,SSDP(Simple Service Discovery Protocol)是 UPnP協(xié)議中的關(guān)鍵協(xié)議,用于設(shè)備發(fā)現(xiàn)和服務(wù)發(fā)布。它通過(guò)HTTP over UDP的形式在局域網(wǎng)內(nèi)廣播和接收?qǐng)?bào)文,采用多播地址 239.255.255.250和端口 1900。
SSDP報(bào)文主要分為以下幾類:
NOTIFY消息(設(shè)備主動(dòng)廣播通知):用于設(shè)備向網(wǎng)絡(luò)通告自己的存在或離線狀態(tài)。
M-SEARCH消息(客戶端主動(dòng)搜索):客戶端發(fā)送搜索請(qǐng)求以發(fā)現(xiàn)設(shè)備或服務(wù)。
HTTP/1.1響應(yīng)消息(設(shè)備對(duì) M-SEARCH的響應(yīng)):設(shè)備對(duì)搜索請(qǐng)求的響應(yīng),提供設(shè)備描述文件的位置及服務(wù)信息。
SSDP報(bào)文基于HTTP協(xié)議,有固定的格式,主要包括以下字段:
HOST:目標(biāo)地址和端口,固定為 239.255.255.250:1900。
MAN:用于標(biāo)識(shí)搜索消息,固定為 "ssdp:discover"(僅在 M-SEARCH中使用)。
MX:最大響應(yīng)時(shí)間,指定設(shè)備在多長(zhǎng)時(shí)間內(nèi)響應(yīng)(單位:秒)。
ST:搜索目標(biāo),標(biāo)識(shí)要查找的設(shè)備類型或服務(wù)類型。
NT:通知類型,表示設(shè)備或服務(wù)的類型(在 NOTIFY消息中使用)。
USN:唯一服務(wù)名稱,設(shè)備或服務(wù)的唯一標(biāo)識(shí)符。
LOCATION:設(shè)備描述文件的 URL,包含設(shè)備的詳細(xì)信息。
CACHE-CONTROL:設(shè)備信息的緩存時(shí)間,表示在多長(zhǎng)時(shí)間內(nèi)有效。
M-SEARCH請(qǐng)求報(bào)文實(shí)例:
M-SEARCH * HTTP/1.1 Host:239.255.255.250:1900 ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1 Man:"ssdp:discover" MX:3
字段解析:
M-SEARCH * HTTP/1.1:表明是一個(gè)搜索請(qǐng)求。
Host:多播地址和端口。
ST:搜索目標(biāo)類型,這里是IGD設(shè)備。
MX:最大響應(yīng)事件,設(shè)備需要在3秒內(nèi)返回響應(yīng)。
Man:搜索請(qǐng)求類型,固定。
M-SEARCH響應(yīng)報(bào)文實(shí)例:
HTTP/1.1 200 OK CACHE-CONTROL: max-age=60 DATE: Tue, 07 Jan 2025 06:43:49 GMT EXT: LOCATION: http://192.168.100.1:1900/igd.xml SERVER: vxWorks/5.5 UPnP/1.0 TL-WR886N/6.0 ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1 USN: uuid:8c15e41f-3d83-41c1-b35d-5D2A64377DE9::urn:schemas-upnp-org:device:InternetGatewayDevice:1
HTTP/1.1 200 OK:表示響應(yīng)成功。
CACHE-CONTROL:響應(yīng)有效時(shí)間為60秒。
DATE:響應(yīng)的時(shí)間戳。
EXT:保留字段,目前為空。
LOCATION:設(shè)備描述文件的URL。
SERVER:設(shè)備的操作系統(tǒng),UPnP版本和設(shè)備名稱。
ST:搜索目標(biāo)類型,和請(qǐng)求中的ST字段一致。
USN:唯一設(shè)備標(biāo)識(shí)符。
獲取設(shè)備標(biāo)識(shí)符
這一步會(huì)通過(guò)HTTP GET方式去請(qǐng)求xml文件,有關(guān)HTTP GET報(bào)文以及HTTP響應(yīng)報(bào)文這里不過(guò)多講解,有興趣的可以參考 HTTP Client章節(jié)。
請(qǐng)求示例:
GET /igd.xml HTTP/1.1 Accept: text/xml, application/xml User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1) Host: 192.168.100.1:1900 Connection: Keep-Alive Cache-Control: no-cache Pragma: no-cache
響應(yīng)示例:
HTTP/1.1 200 OK Content-Type: text/xml;charset=UTF-8 Content-Length: 2580 Connection: close Cache-control: no-cache ?xml version="1.0"??> 1/major?> 0/minor?> /specVersion?> urn:schemas-upnp-org:device:InternetGatewayDevice:1/deviceType?> http://192.168.100.1:80 /presentationURL?> Wireless N Router TL-WR886N/friendlyName?> TP-LINK/manufacturer?> http://www.tp-link.com.cn/manufacturerURL?> TL-WR886N 6.0/modelDescription?> TL-WR886N/modelName?> 6.0/modelNumber?> uuid:8c15e41f-3d83-41c1-b35d-5D2A64377DE9/UDN?> 123456789001/UPC?> urn:schemas-upnp-org:service:Layer3Forwarding:1/serviceType?> urn:upnp-org:serviceId:L3Forwarding1/serviceId?> /l3f/controlURL?> /l3f/eventSubURL?> /l3f.xml/SCPDURL?> /service?> /serviceList?> urn:schemas-upnp-org:device:WANDevice:1/deviceType?> WAN Device/friendlyName?> TP-LINK/manufacturer?> http://www.tp-link.com.cn/manufacturerURL?> WAN Device/modelDescription?> WAN Device/modelName?> 1.0/modelNumber?> /modelURL?> 12345678900001/serialNumber?> uuid:8c15e41f-3d83-41c1-b35d-5D2A64377DE9/UDN?> 123456789001/UPC?> urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1/serviceType?> urn:upnp-org:serviceId:WANCommonInterfaceConfig/serviceId?> /ifc/controlURL?> /ifc/eventSubURL?> /ifc.xml/SCPDURL?> /service?> /serviceList?> urn:schemas-upnp-org:device:WANConnectionDevice:1/deviceType?> WAN Connection Device/friendlyName?> TP-LINK/manufacturer?> http://www.tp-link.com.cn/manufacturerURL?> WAN Connection Device/modelDescription?> WAN Connection Device/modelName?> 1.0/modelNumber?> /modelURL?> 12345678900001/serialNumber?> uuid:8c15e41f-3d83-41c1-b35d-5D2A64377DE9/UDN?> 123456789001/UPC?> urn:schemas-upnp-org:service:WANIPConnection:1/serviceType?> urn:upnp-org:serviceId:WANIPConnection/serviceId?> /ipc/controlURL?> /ipc/eventSubURL?> /ipc.xml/SCPDURL?> /service?> /serviceList?> /device?> /deviceList?> /device?> /deviceList?> /device?> /root?>
訂閱IGD事件
通過(guò)HTTP SUBSCRIBE訂閱IGD事件,示例:
SUBSCRIBE /ipc HTTP/1.1 Host: 192.168.100.1:1900 USER-AGENT: Mozilla/4.0 (compatible; UPnP/1.1; Windows NT/5.1) CALLBACK: NT: upnp:event TIMEOUT: Second-1800
響應(yīng)示例:
HTTP/1.1 200 OK Content-Type: text/xml;charset=UTF-8 Content-Length: 0 Connection: close Cache-control: no-cache Server: vxWorks/5.5 UPnP/1.0 TL-WR886N/6.0 Timeout: Second-1800 SID: uuid:82-2150160019
添加映射端口報(bào)文
例如,我們想映射TCP協(xié)議的內(nèi)部端口8000到外部端口1000上,可以按照以下示例進(jìn)行HTTP請(qǐng)求:
POST /ipc HTTP/1.1 Content-Type: text/xml; charset="utf-8" SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping" User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1) Host: 192.168.100.1:1900 Content-Length: 1131 Connection: Keep-Alive Cache-Control: no-cache Pragma: no-cache ?xml version="1.0"??> /NewRemoteHost?> 1000/NewExternalPort?> TCP/NewProtocol?> 8000/NewInternalPort?> 192.168.100.101/NewInternalClient?> 1/NewEnabled?> W5500_uPnPGetway/NewPortMappingDescription?> 0/NewLeaseDuration?> /m:AddPortMapping?> /SOAP-ENV:Body?> /SOAP-ENV:Envelope?>
主要字段的描述如下:
m:AddPortMapping:添加端口映射
NewExternalPort:外部端口號(hào)
NewProtocol:協(xié)議類型
NewInternalPort:內(nèi)部端口號(hào)
NewInternalClient:內(nèi)部地址
響應(yīng)內(nèi)容:
HTTP/1.1 200 OK Content-Type: text/xml;charset=UTF-8 Content-Length: 289 Connection: close Cache-control: no-cache Server: vxWorks/5.5 UPnP/1.0 TL-WR886N/6.0 ?xml version="1.0"??> /u:AddPortMappingResponse?>/s:Body?>/s:Envelope?>
刪除端口映射報(bào)文
例如,我們想刪除上面映射的1000端口,可以按照以下示例進(jìn)行HTTP請(qǐng)求:
POST /ipc HTTP/1.1 Content-Type: text/xml; charset="utf-8" SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping" User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1) Host: 192.168.100.1:1900 Content-Length: 604 Connection: Keep-Alive Cache-Control: no-cache Pragma: no-cache ?xml version="1.0"??> /NewRemoteHost?> 1000/NewExternalPort?> TCP/NewProtocol?> /m:DeletePortMapping?> /SOAP-ENV:Body?>/SOAP-ENV:Envelope?>
主要字段的描述如下:
m:DeletePortMapping:刪除端口映射
NewExternalPort:外部端口號(hào)
NewProtocol:協(xié)議類型
NewRemoteHost:外部訪問(wèn)來(lái)源,可以為空
6實(shí)現(xiàn)過(guò)程
在這個(gè)例程中,我們實(shí)現(xiàn)了通過(guò)串口控制LED燈開(kāi)關(guān)、獲取和設(shè)置網(wǎng)絡(luò)地址信息、TCP和UDP回環(huán)數(shù)據(jù)測(cè)試以及UPnP添加映射端口和刪除映射端口的功能。
注意:測(cè)試實(shí)例需要W55MH32接入在支持UPnP端口轉(zhuǎn)發(fā)的路由器下。
步驟1:設(shè)置以太網(wǎng)緩存大小
static uint8_t tx_size[_WIZCHIP_SOCK_NUM_] = {4, 4, 2, 1, 1, 1, 1, 2};
static uint8_t rx_size[_WIZCHIP_SOCK_NUM_] = {4, 4, 2, 1, 1, 1, 1, 2};
/* socket rx and tx buff init */
wizchip_init(tx_size, rx_size);
在這里我們給socket0-7的收發(fā)緩存分別設(shè)置為4KB,4KB,2KB,1KB,1KB,1KB,1KB,2KB。
其中socket0用于UPnP協(xié)議處理,socket1用于TCP和UDP回環(huán)處理,socket2用于監(jiān)聽(tīng)I(yíng)GD事件。
步驟2:LED控制函數(shù)注冊(cè)
UserLED_Control_Init(set_user_led_status);
set_user_led_status()函數(shù)為控制LED的函數(shù),具體內(nèi)容如下:
/*void set_user_led_status(uint8_t val)
{
if (val)
{
GPIO_SetBits(GPIOD, GPIO_Pin_14);
}
else
{
GPIO_ResetBits(GPIOD, GPIO_Pin_14);
}
}
步驟3:搜索UPnP設(shè)備
do
{
printf("Send SSDP.. rn");
} while (SSDPProcess(SOCKET_ID) != 0); // SSDP Search discovery
/**< SSDP Header */
unsigned char SSDP[] = "
M-SEARCH * HTTP/1.1rn
Host:239.255.255.250:1900rn
ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1rn
Man:"ssdp:discover"rn
MX:3rn
rn
";
/**
* @brief This function processes the SSDP message.
* @return 0: success, -1: reply packet timeout, 1: received SSDP parse error
*/
signed char SSDPProcess(SOCKET sockfd)
{
char ret_value = 0;
long endTime = 0;
unsigned char mcast_addr[4] = {239, 255, 255, 250};
// unsigned char_t_t mcast_mac[6] = {0x28, 0x2C, 0xB2, 0xE9, 0x42, 0xD6};
unsigned char recv_addr[4];
unsigned short recv_port;
// UDP Socket Open
close(sockfd);
socket(sockfd, Sn_MR_UDP, PORT_SSDP, 0); /*Initialize socket for socket 0*/
while (getSn_SR(sockfd) != SOCK_UDP);
#ifdef UPNP_DEBUG
printf("%srn", SSDP);
#endif
// Send SSDP
if (sendto(sockfd, SSDP, strlen((char *)SSDP), mcast_addr, 1900) <= 0)
printf("SSDP Send error!!!!!!!rn");
// Receive Reply
memset(recv_buffer, '', RECV_BUFFER_SIZE);
endTime = my_time + 3;
while (recvfrom(sockfd, (unsigned char *)recv_buffer, RECV_BUFFER_SIZE, recv_addr, &recv_port) <= 0 && my_time < endTime); // Check Receive Buffer
if (my_time >= endTime)
{ // Check Timeout
close(sockfd);
return -1;
}
// UDP Socket Close
close(sockfd);
#ifdef UPNP_DEBUG
printf("rnReceiveDatarn%srn", recv_buffer);
#endif
// Parse SSDP Message
if ((ret_value = parseSSDP(recv_buffer)) == 0)
UPnP_Step = 1;
return ret_value;
}
在這個(gè)函數(shù)中,主要是使用SSDP協(xié)議搜索IGD設(shè)備,發(fā)送報(bào)文和前面我們介紹的一致。
步驟4:獲取IGD設(shè)備描述
if (GetDescriptionProcess(SOCKET_ID) == 0) // GET IGD description
{
printf("GetDescription Success!!rn");
}
else
{
printf("GetDescription Fail!!rn");
}
/**
* @brief This function gets the description message from IGD(Internet Gateway Device).
* @return 0: success, -2: Invalid UPnP Step, -1: reply packet timeout, 1: received xml parse error
*/
signed char GetDescriptionProcess(
SOCKET sockfd /**< a socket number. */
)
{
char ret_value = 0;
long endTime = 0;
unsigned long ipaddr;
unsigned short port;
// Check UPnP Step
if (UPnP_Step < 1) return -2;
// Make HTTP GET Header
memset(send_buffer, '', SEND_BUFFER_SIZE);
MakeGETHeader(send_buffer);
#ifdef UPNP_DEBUG
printf("%srn", send_buffer);
#endif
ipaddr = inet_addr((unsigned char *)descIP);
ipaddr = swapl(ipaddr);
port = ATOI(descPORT, 10);
// Connect to IGD(Internet Gateway Device)
close(sockfd);
socket(sockfd, Sn_MR_TCP, PORT_UPNP, Sn_MR_ND); /*Open a port of the socket*/
while (getSn_SR(sockfd) != SOCK_INIT)
{
delay_ms(100);
}
if (connect(sockfd, (unsigned char *)&ipaddr, port) == 0)
printf("TCP Socket Error!!rn");
// Send Get Discription Message
while ((getSn_SR(sockfd) != SOCK_ESTABLISHED));
send(sockfd, (void *)send_buffer, strlen(send_buffer));
// Receive Reply
memset(recv_buffer, '', RECV_BUFFER_SIZE);
delay_ms(500);
endTime = my_time + 3;
while (recv(sockfd, (void *)recv_buffer, RECV_BUFFER_SIZE) <= 0 && my_time < endTime); // Check Receive Buffer
if (my_time >= endTime)
{ // Check Timeout
close(sockfd);
return -1;
}
// TCP Socket Close
close(sockfd);
#ifdef UPNP_DEBUG
printf("rnReceiveDatarn%srn", recv_buffer);
#endif
// Parse Discription Message
if ((ret_value = parseDescription(recv_buffer)) == 0) UPnP_Step = 2;
return ret_value;
}
請(qǐng)求報(bào)文通過(guò)MakeGETHeader()函數(shù)進(jìn)行組包,具體報(bào)文如下:
/**
* @brief This function makes the HTTP GET header.
* @param dest:Target string pointer
* @return none
*/
void MakeGETHeader(char *dest)
{
char local_port[6] = {''};
strcat(dest, "GET ");
strcat(dest, descLOCATION);
strcat(dest, " HTTP/1.1rn");
strcat(dest, "Accept: text/xml, application/xmlrn");
strcat(dest, "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)rn");
strcat(dest, "Host: ");
strcat(dest, descIP);
sprintf(local_port, ":%s", descPORT);
strcat(dest, local_port);
strcat(dest, "rnConnection: Keep-AlivernCache-Control: no-cachernPragma: no-cachernrn");
}
然后將接收到的內(nèi)容,通過(guò)parseDescription()函數(shù)進(jìn)行解析,如果設(shè)備描述中不支持WANIPConnection服務(wù),則說(shuō)明不支持端口映射,返回錯(cuò)誤。
parseDescription()函數(shù)內(nèi)容如下:
/**
* @brief This function parses the received description message from IGD(Internet Gateway Dev
* @return 0: success, 1: received xml parse error
*/
signed char parseDescription(
const char *xml /**< string for parse */
)
{
const char controlURL_[] = "";
const char eventSubURL_[] = "";
char *URL_start = 0, *URL_end = 0;
if (parseHTTP(xml) != 0) return 1;
//printf("rn%srn", xml);
// Find Control URL("/etc/linuxigd/gateconnSCPD.ctl")
if ((URL_start = strstr(xml, "urn:schemas-upnp-org:service:WANIPConnection:1")) == NULL) retur
if ((URL_start = strstr(URL_start, controlURL_)) == NULL) return 1;
if ((URL_end = strstr(URL_start, "")) == NULL) return 1;
strncpy(controlURL, URL_start + strlen(controlURL_), URL_end - URL_start - strlen(controlURL_)
// Find Eventing Subscription URL("/etc/linuxigd/gateconnSCPD.evt")
if ((URL_start = strstr(xml, "urn:schemas-upnp-org:service:WANIPConnection:1")) == NULL) retur
if ((URL_start = strstr(URL_start, eventSubURL_)) == NULL) return 1;
if ((URL_end = strstr(URL_start, "")) == NULL) return 1;
strncpy(eventSubURL, URL_start + strlen(eventSubURL_), URL_end - URL_start - strlen(eventSubUR
return 0;
}
步驟5:訂閱IGD事件
if (SetEventing(SOCKET_ID) == 0) // Subscribes IGD event messages
{
printf("SetEventing Success!!rn");
}
else
{
printf("SetEventing Fail!!rn");
}
SetEventing()函數(shù)內(nèi)容如下:
/**
* @brief This function subscribes to the eventing message from IGD(Internet Gateway Device).
* @return 0: success, -2: Invalid UPnP Step, -1: reply packet timeout
*/
signed char SetEventing(
SOCKET sockfd /**< a socket number. */
)
{
long endTime = 0;
unsigned long ipaddr;
unsigned short port;
// Check UPnP Step
if (UPnP_Step < 2) return -2;
// Make Subscription message
memset(send_buffer, '', SEND_BUFFER_SIZE);
MakeSubscribe(send_buffer, PORT_UPNP_EVENTING);
#ifdef UPNP_DEBUG
printf("%srn", send_buffer);
#endif
ipaddr = inet_addr((unsigned char *)descIP);
ipaddr = swapl(ipaddr);
port = ATOI(descPORT, 10);
// Connect to IGD(Internet Gateway Device)
close(sockfd);
socket(sockfd, Sn_MR_TCP, PORT_UPNP, Sn_MR_ND); /*Open a port of the socket*/
while (getSn_SR(sockfd) != SOCK_INIT)
{
delay_ms(100);
}
if (connect(sockfd, (unsigned char *)&ipaddr, port) == 0)
printf("TCP Socket Error!!rn");
// Send Get Discription Message
while ((getSn_SR(sockfd) != SOCK_ESTABLISHED));
send(sockfd, (void *)send_buffer, strlen(send_buffer));
// Receive Reply
memset(recv_buffer, '', RECV_BUFFER_SIZE);
delay_ms(500);
endTime = my_time + 3;
while (recv(sockfd, (void *)recv_buffer, RECV_BUFFER_SIZE) <= 0 && my_time < endTime); // Check Receive Buffer
if (my_time >= endTime)
{ // Check Timeout
close(sockfd);
return -1;
}
// TCP Socket Close
close(sockfd);
#ifdef UPNP_DEBUG
printf("rnReceiveDatarn%srn", recv_buffer);
#endif
return parseHTTP(recv_buffer);
}
請(qǐng)求報(bào)文通過(guò)MakeSubscribe()函數(shù)進(jìn)行組包,具體報(bào)文如下:
/**
* @brief This function makes the Subscription message.
* @param dest:Target string pointer
* @param listen_port:Listen port
* @return none
*/
void MakeSubscribe(char *dest, const unsigned int listen_port)
{
char local_port[6] = {''}, ipaddr[16] = {''};
unsigned char ip[4];
strcat(dest, "SUBSCRIBE ");
strcat(dest, eventSubURL);
strcat(dest, " HTTP/1.1rn");
strcat(dest, "Host: ");
strcat(dest, descIP);
sprintf(local_port, ":%s", descPORT);
strcat(dest, local_port);
strcat(dest, "rnUSER-AGENT: Mozilla/4.0 (compatible; UPnP/1.1; Windows NT/5.1)rn");
strcat(dest, "CALLBACK: ", listen_port);
strcat(dest, local_port);
strcat(dest, "rnNT: upnp:eventrnTIMEOUT: Second-1800rnrn");
}
最后,通過(guò)parseHTTP()函數(shù)解析HTTP響應(yīng)報(bào)文,判斷是否訂閱成功。
parseHTTP()函數(shù)如下:
/*-----String Parse Functions-----*/
/**
* @brief This function parses the HTTP header.
* @return 0: success, 1: received xml parse error
*/
signed char parseHTTP(
const char *xml /**< string for parse */
)
{
char *loc = 0;
if (strstr(xml, "200 OK") != NULL)
return 0;
else
{
loc = strstr(xml, "rn");
memset(content, '', CONT_BUFFER_SIZE);
strncpy(content, xml, loc - xml);
printf("rnHTTP Error:rn%srnrn", content);
return 1;
}
}
步驟6:執(zhí)行UPnP主程序
Main_Menu(SOCKET_ID, SOCKET_ID + 1, SOCKET_ID + 2, ethernet_buf, tcps_port, udps_port); // Main menu
/**
* @brief Display/Manage a Menu on HyperTerminal Window
* @param sn: use for SSDP; sn2: use for run tcp/udp loopback; sn3: use for listenes IGD event message
* @param buf: use for tcp/udp loopback rx/tx buff; tcps_port: use for tcp loopback listen; udps_port: use
for udp loopback receive
* @return none
*/
void Main_Menu(uint8_t sn, uint8_t sn2, uint8_t sn3, uint8_t *buf, uint16_t tcps_port, uint16_t udps_port)
{
static char choice[3];
static char msg[256], ipaddr[12], protocol[4];
static unsigned short ret, external_port, internal_port;
static uint8_t bTreat;
static uint8_t Sip[4];
while (1)
{
/* Display Menu on HyperTerminal Window */
bTreat = RESET;
printf("rn====================== WIZnet Chip Control Point ===================rn");
printf("This Application is basic example of UART interface withrn");
printf("Windows Hyper Terminal. rn");
printf("rn==========================================================rn");
printf(" APPLICATION MENU :rn");
printf("rn==========================================================rnn");
printf(" 1 - Set LED on rn");
printf(" 2 - Set LED off rn");
printf(" 3 - Show network settingrn");
printf(" 4 - Set network settingrn");
printf(" 5 - Run TCP Loopbackrn");
printf(" 6 - Run UDP Loopbackrn");
printf(" 7 - UPnP PortForwarding: AddPortrn");
printf(" 8 - UPnP PortForwarding: DeletePortrn");
printf("Enter your choice : ");
memset(choice, 0, sizeof(choice));
scanf("%s", choice);
printf("%crn", choice[0]);
在這里會(huì)執(zhí)行一個(gè)用戶選項(xiàng)菜單,選項(xiàng)1和2控制LED開(kāi)關(guān),選項(xiàng)3和4打印和設(shè)置網(wǎng)絡(luò)地址信息,選項(xiàng)5運(yùn)行一個(gè)TCP回環(huán)測(cè)試程序(回環(huán)測(cè)試程序可參考TCP Server章節(jié)),選項(xiàng)6運(yùn)行一個(gè)UDP回環(huán)測(cè)試程序(回環(huán)測(cè)試程序可參考UDP章節(jié))。選項(xiàng)7添加一個(gè)UPnP端口映射表,選項(xiàng)8刪除一個(gè)UPnP端口映射表。這里我們主要講解UPnP相關(guān)的選項(xiàng)7和選項(xiàng)8。
步驟7:添加一個(gè)UPnP端口映射表
代碼如下:
if (choice[0] == '7')
{
bTreat = SET;
printf("rnType a Protocol(TCP/UDP) : ");
memset(msg, 0, sizeof(msg));
scanf("%s", msg);
printf("%srn", msg);
strncpy(protocol, msg, 3);
protocol[3] = '';
printf("rnType a External Port Number : ");
memset(msg, 0, sizeof(msg));
scanf("%s", msg);
printf("%srn", msg);
external_port = ATOI(msg, 10);
printf("rnType a Internal Port Number : ");
memset(msg, 0, sizeof(msg));
scanf("%s", msg);
printf("%srn", msg);
internal_port = ATOI(msg, 10);
if(strcmp(protocol,"tcp") || strcmp(protocol,"TCP"))
tcps_port = internal_port;
else
udps_port = internal_port;
close(sn2);
// Try to Add Port Action
getSIPR(Sip);
sprintf(ipaddr, "%d.%d.%d.%d", Sip[0], Sip[1], Sip[2], Sip[3]);
if ((ret = AddPortProcess(sn, protocol, external_port, ipaddr, internal_port, "W5500_uPnPGetway")) == 0)
printf("AddPort Success!!rn");
else
printf("AddPort Error Code is %drn", ret);
}
在這里,我們需要外部輸入端口映射的協(xié)議類型(TCP或UDP),以及外部端口號(hào)和內(nèi)部端口號(hào)。輸入完成后,選項(xiàng)5或選項(xiàng)6的端口號(hào)會(huì)替換為輸入的內(nèi)部端口號(hào),然后通過(guò)AddPortProcess()函數(shù)執(zhí)行添加端口映射處理。AddPortProcess()函數(shù)內(nèi)容如下:
/**
* @brief This function processes the add port to IGD(Internet Gateway Device).
* @return 0: success, -2: Invalid UPnP Step, -1: reply packet timeout, 1: received xml parse error, other: UPnP error code
*/
signed short AddPortProcess(
SOCKET sockfd, /**< a socket number. */
const char *protocol, /**< a procotol name. "TCP" or "UDP" */
const unsigned int extertnal_port, /**< an external port number. */
const char *internal_ip, /**< an internal ip address. */
const unsigned int internal_port, /**< an internal port number. */
const char *description /**< a description of this portforward. */
)
{
short len = 0;
long endTime = 0;
unsigned long ipaddr;
unsigned short port;
// Check UPnP Step
if (UPnP_Step < 2) return -2;
// Make "Add Port" XML(SOAP)
memset(content, '', CONT_BUFFER_SIZE);
MakeSOAPAddControl(content, protocol, extertnal_port, internal_ip, internal_port, description);
// Make HTTP POST Header
memset(send_buffer, '', SEND_BUFFER_SIZE);
len = strlen(content);
MakePOSTHeader(send_buffer, len, ADD_PORT);
strcat(send_buffer, content);
//#ifdef UPNP_DEBUG
printf("%srn", send_buffer);
//#endif
ipaddr = inet_addr((unsigned char *)descIP);
ipaddr = swapl(ipaddr);
port = ATOI(descPORT, 10);
// Connect to IGD(Internet Gateway Device)
socket(sockfd, Sn_MR_TCP, PORT_UPNP, Sn_MR_ND); /*Open a port of the socket*/
while (getSn_SR(sockfd) != SOCK_INIT);
if (connect(sockfd, (unsigned char *)&ipaddr, port) == 0)
printf("TCP Socket Error!!rn");
// Send "Delete Port" Message
while (getSn_SR(sockfd) != SOCK_ESTABLISHED);
send(sockfd, (void *)send_buffer, strlen(send_buffer));
// Receive Reply
memset(recv_buffer, '', RECV_BUFFER_SIZE);
delay_ms(500);
endTime = my_time + 3;
while (recv(sockfd, (void *)recv_buffer, RECV_BUFFER_SIZE) <= 0 && my_time < endTime); // Check Receive Buffer
if (my_time >= endTime)
{ // Check Timeout
close(sockfd);
return -1;
}
// TCP Socket Close
close(sockfd);
//#ifdef UPNP_DEBUG
printf("rnReceiveDatarn%srn", recv_buffer);
//#endif
// Parse Replied Message
return parseAddPort(recv_buffer);
}
程序首先會(huì)通過(guò)MakeSOAPAddControl()函數(shù)組裝請(qǐng)求報(bào)文中的XML部分,具體內(nèi)容如下:
/**< SOAP header & tail */
const char soap_start[] =
"
?xml version="1.0"??>rn
";
const char soap_end[] =
"
/SOAP-ENV:Body?>/SOAP-ENV:Envelope?>rn
";
/**< Delete Port Mapping */
const char DeletePortMapping_[] = "";
const char _DeletePortMapping[] = "/m:DeletePortMapping?>";
/**< New Remote Host */
const char NewRemoteHost_[] = "";
const char _NewRemoteHost[] = "/NewRemoteHost?>";
/**< New External Port */
const char NewExternalPort_[] = "";
const char _NewExternalPort[] = "/NewExternalPort?>";
/**< New Protocol */
const char NewProtocol_[] = "";
const char _NewProtocol[] = "/NewProtocol?>";
/**< Add Port Mapping */
const char AddPortMapping_[] = "";
const char _AddPortMapping[] = "/m:AddPortMapping?>";
/**< New Internal Port */
const char NewInternalPort_[] = "";
const char _NewInternalPort[] = "/NewInternalPort?>";
/**< New Internal Client */
const char NewInternalClient_[] = "";
const char _NewInternalClient[] = "/NewInternalClient?>";
/**< New Enabled */
const char NewEnabled[] = "1/NewEnabled?>";
const char NewEnabled_[] = "";
const char _NewEnabled[] = "/NewEnabled?>";
/**< New Port Mapping Description */
const char NewPortMappingDescription_[] = "";
const char _NewPortMappingDescription[] = "/NewPortMappingDescription?>";
/**< New Lease Duration */
const char NewLeaseDuration[] = "0/NewLeaseDuration?>";
const char NewLeaseDuration_[] = "";
const char _NewLeaseDuration[] = "/NewLeaseDuration?>";
/**
* @brief This function makes the Add Port Control message in SOAP.
* @param dest:Target string pointer
* @param protocol:Protocol type
* @param extertnal_port:External port
* @param internal_ip:Internal IP address
* @param internal_port:Internal port
* @param description:Description
* @return none
*/
void MakeSOAPAddControl(char *dest, const char *protocol, const unsigned int extertnal_port, const char * internal_ip, const unsigned int internal_port, const char *description)
{
char local_port[6] = {''};
strcat(dest, soap_start);
strcat(dest, AddPortMapping_);
strcat(dest, NewRemoteHost_);
strcat(dest, _NewRemoteHost);
strcat(dest, NewExternalPort_);
sprintf(local_port, "%d", extertnal_port);
strcat(dest, local_port);
strcat(dest, _NewExternalPort);
strcat(dest, NewProtocol_);
strcat(dest, protocol);
strcat(dest, _NewProtocol);
strcat(dest, NewInternalPort_);
sprintf(local_port, "%d", internal_port);
strcat(dest, local_port);
strcat(dest, _NewInternalPort);
strcat(dest, NewInternalClient_);
strcat(dest, internal_ip);
strcat(dest, _NewInternalClient);
strcat(dest, NewEnabled);
strcat(dest, NewPortMappingDescription_);
strcat(dest, description);
strcat(dest, _NewPortMappingDescription);
strcat(dest, NewLeaseDuration);
strcat(dest, _AddPortMapping);
strcat(dest, soap_end);
}
然后通過(guò)MakePOSTHeader()函數(shù)制作HTTP頭部?jī)?nèi)容,具體內(nèi)容如下:
/**
* @brief This function makes the HTTP POST Header.
* @param dest:Target string pointer
* @param content_length: content length
* @param action: action type
* @return none
*/
void MakePOSTHeader(char *dest, int content_length, int action)
{
char local_length[6] = {''}, local_port[6] = {''};
sprintf(local_length, "%d", content_length);
strcat(dest, "POST ");
strcat(dest, controlURL);
strcat(dest, " HTTP/1.1rn");
strcat(dest, "Content-Type: text/xml; charset="utf-8"rn");
strcat(dest, "SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#");
switch (action)
{
case DELETE_PORT:
strcat(dest, "DeletePortMapping"");
break;
case ADD_PORT:
strcat(dest, "AddPortMapping"");
break;
}
strcat(dest, "rnUser-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)rn");
strcat(dest, "Host: ");
strcat(dest, descIP);
sprintf(local_port, ":%s", descPORT);
strcat(dest, local_port);
strcat(dest, "rnContent-Length: ");
strcat(dest, local_length);
strcat(dest, "rnConnection: Keep-AlivernCache-Control: no-cachernPragma: no-cachernrn");
}
最后則是發(fā)送請(qǐng)求,然后通過(guò)parseAddPort()函數(shù)解析響應(yīng)內(nèi)容判斷是否添加端口映射成功。
signed short parseAddPort(
const char *xml /**< string for parse */
)
{
parseHTTP(xml);
if (strstr(xml, "u:AddPortMappingResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"") == NULL)
{
return parseError(xml);
}
return 0;
}
步驟8:刪除一個(gè)UPnP端口映射表
if (choice[0] == '8')
{
bTreat = SET;
printf("rnType a Protocol(TCP/UDP) : ");
memset(msg, 0, sizeof(msg));
scanf("%s", msg);
printf("%srn", msg);
//GetInputString(msg);
strncpy(protocol, msg, 3);
protocol[3] = '';
printf("rnType a External Port Number : ");
// TCP_LISTEN_PORT=num;
// UDP_LISTEN_PORT=num;
// printf("%drn",TCP_LISTEN_PORT);
memset(msg, 0, sizeof(msg));
scanf("%s", msg);
printf("%srn", msg);
external_port = ATOI(msg, 10);
// Try to Delete Port Action
if ((ret = DeletePortProcess(sn, protocol, external_port)) == 0)
printf("DeletePort Success!!rn");
else
printf("DeletePort Error Code is %drn", ret);
}
/* OTHERS CHOICE*/
if (bTreat == RESET)
{
printf(" wrong choice rn");
}
在這里,我們需要外部輸入刪除端口映射的協(xié)議類型(TCP或UDP),以及外部端口號(hào)。輸入完成后,通過(guò)DeletePortProcess()函數(shù)執(zhí)行添加端口映射處理。DeletePortProcess()函數(shù)內(nèi)容如下:
/**
* @brief This function processes the delete port to IGD(Internet Gateway Device).
* @return 0: success, -2: Invalid UPnP Step, -1: reply packet timeout, 1: received xml parse error, other: UPnP error code
*/
signed short DeletePortProcess(
SOCKET sockfd, /**< a socket number. */
const char *protocol, /**< a procotol name. "TCP" or "UDP" */
const unsigned int extertnal_port /**< an external port number. */
)
{
short len = 0;
long endTime = 0;
unsigned long ipaddr;
unsigned short port;
// Check UPnP Step
if (UPnP_Step < 2) return -2;
// Make "Delete Port" XML(SOAP)
memset(content, '', CONT_BUFFER_SIZE);
MakeSOAPDeleteControl(content, protocol, extertnal_port);
// Make HTTP POST Header
memset(send_buffer, '', SEND_BUFFER_SIZE);
len = strlen(content);
MakePOSTHeader(send_buffer, len, DELETE_PORT);
strcat(send_buffer, content);
//#ifdef UPNP_DEBUG
printf("%srn", send_buffer);
//#endif
ipaddr = inet_addr((unsigned char *)descIP);
ipaddr = swapl(ipaddr);
port = ATOI(descPORT, 10);
// Connect to IGD(Internet Gateway Device)
close(sockfd);
socket(sockfd, Sn_MR_TCP, PORT_UPNP, Sn_MR_ND); /*Open a port of the socket*/
while (getSn_SR(sockfd) != SOCK_INIT);
if (connect(sockfd, (unsigned char *)&ipaddr, port) == 0)
printf("TCP Socket Error!!rn");
// Send "Delete Port" Message
while (getSn_SR(sockfd) != SOCK_ESTABLISHED);
send(sockfd, (void *)send_buffer, strlen(send_buffer));
// Receive Reply
memset(recv_buffer, '', RECV_BUFFER_SIZE);
delay_ms(500);
endTime = my_time + 3;
while (recv(sockfd, (void *)recv_buffer, RECV_BUFFER_SIZE) <= 0 && my_time < endTime); // Check Receive Buffer
if (my_time >= endTime)
{ // Check Timeout
close(sockfd);
return -1;
}
// TCP Socket Close
close(sockfd);
//#ifdef UPNP_DEBUG
printf("rnReceiveDatarn%srn", recv_buffer);
//#endif
// Parse Replied Message
return parseDeletePort(recv_buffer);
}
首先會(huì)通過(guò)MakeSOAPDeleteControl()函數(shù)組裝請(qǐng)求報(bào)文中的XML部分,具體內(nèi)容如下:
/**
* @brief This function makes the Delete Port Control message in SOAP.
* @param dest:Target string pointer
* @param protocol:Protocol type
* @param extertnal_port:External port
* @return none
*/
void MakeSOAPDeleteControl(char *dest, const char *protocol, const unsigned int extertnal_port)
{
char local_port[6] = {''};
strcat(dest, soap_start);
strcat(dest, DeletePortMapping_);
strcat(dest, NewRemoteHost_);
strcat(dest, _NewRemoteHost);
strcat(dest, NewExternalPort_);
sprintf(local_port, "%d", extertnal_port);
strcat(dest, local_port);
strcat(dest, _NewExternalPort);
strcat(dest, NewProtocol_);
strcat(dest, protocol);
strcat(dest, _NewProtocol);
strcat(dest, _DeletePortMapping);
strcat(dest, soap_end);
}
然后通過(guò)MakePOSTHeader()函數(shù)制作HTTP頭部?jī)?nèi)容,具體內(nèi)容如下:
/**
* @brief This function makes the HTTP POST Header.
* @param dest:Target string pointer
* @param content_length: content length
* @param action: action type
* @return none
*/
void MakePOSTHeader(char *dest, int content_length, int action)
{
char local_length[6] = {''}, local_port[6] = {''};
sprintf(local_length, "%d", content_length);
strcat(dest, "POST ");
strcat(dest, controlURL);
strcat(dest, " HTTP/1.1rn");
strcat(dest, "Content-Type: text/xml; charset="utf-8"rn");
strcat(dest, "SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#");
switch (action)
{
case DELETE_PORT:
strcat(dest, "DeletePortMapping"");
break;
case ADD_PORT:
strcat(dest, "AddPortMapping"");
break;
}
strcat(dest, "rnUser-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)rn");
strcat(dest, "Host: ");
strcat(dest, descIP);
sprintf(local_port, ":%s", descPORT);
strcat(dest, local_port);
strcat(dest, "rnContent-Length: ");
strcat(dest, local_length);
strcat(dest, "rnConnection: Keep-AlivernCache-Control: no-cachernPragma: no-cachernrn");
}
最后則是發(fā)送請(qǐng)求,然后通過(guò)parseDeletePort()函數(shù)解析響應(yīng)內(nèi)容判斷是否添加端口映射成功。
signed short parseDeletePort(
const char *xml /**< string for parse */
)
{
parseHTTP(xml);
if (strstr(xml, "u:DeletePortMappingResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"") == NULL)
{
return parseError(xml);
}
return 0;
}
7運(yùn)行結(jié)果
燒錄例程運(yùn)行后,首先進(jìn)行了PHY鏈路檢測(cè),然后是通過(guò)DHCP獲取網(wǎng)絡(luò)地址并打印網(wǎng)絡(luò)地址信息:

接下來(lái)是搜索IGD設(shè)備,搜索成功后會(huì)進(jìn)行獲取設(shè)備描述以及設(shè)置訂閱IGD事件,全部成功后則進(jìn)入主菜單。

接著,我們輸入7,添加一個(gè)TCP協(xié)議的端口映射,外部端口為12345,內(nèi)部端口為8000。

打開(kāi)UPnP Wizard軟件,點(diǎn)擊刷新后可以看到我們添加的端口映射表。(UPnP Wizard下載鏈接:https://upnp-wizard.en.softonic.com/)

然后我們輸入5,打開(kāi)TCP回環(huán)測(cè)試程序。

隨后,我們打開(kāi)一個(gè)網(wǎng)絡(luò)調(diào)試助手,例如SocketTester,選擇為T(mén)CP Client模式,服務(wù)器地址為外部IP地址也就是192.168.1.135,端口號(hào)為外部端口號(hào)12345,點(diǎn)擊”Connect”連接后,可以看到成功連接到內(nèi)部的W55MH32上了。UDP也是同樣進(jìn)行操作,這里不再演示。

接著我們輸入Q退出回環(huán)測(cè)試程序,然后輸入8,將之前添加的TCP協(xié)議的12345外部端口刪除。在UPnP Wizard上點(diǎn)擊刷新,可以看到已經(jīng)成功刪除,再次執(zhí)行回環(huán)測(cè)試程序,已經(jīng)無(wú)法連接上內(nèi)部的W55MH32上。




8總結(jié)
本文講解了如何在 W55MH32芯片上實(shí)現(xiàn) UPnP協(xié)議的端口轉(zhuǎn)發(fā)功能,通過(guò)實(shí)戰(zhàn)例程詳細(xì)展示了從設(shè)備搜索、獲取設(shè)備描述、訂閱事件到添加和刪除端口映射的完整流程,包括各步驟涉及的協(xié)議報(bào)文、函數(shù)實(shí)現(xiàn)和具體操作。文章還對(duì) UPnP協(xié)議的簡(jiǎn)介、特點(diǎn)、應(yīng)用場(chǎng)景進(jìn)行了分析,幫助讀者理解其在網(wǎng)絡(luò)設(shè)備互聯(lián)互通中的實(shí)際應(yīng)用價(jià)值。
下一篇文章將聚焦 TFTP協(xié)議,解析其核心原理及在文件傳輸中的應(yīng)用,同時(shí)講解如何在W55MH32上實(shí)現(xiàn) TFTP功能,敬請(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文章
5876瀏覽量
179035 -
端口
+關(guān)注
關(guān)注
4文章
1092瀏覽量
33666 -
UPnP
+關(guān)注
關(guān)注
0文章
7瀏覽量
8600
發(fā)布評(píng)論請(qǐng)先 登錄
第二十六章 W55MH32?上位機(jī)搜索和配置示例
第二十三章 W55MH32 MQTT_OneNET示例
第十八章 W55MH32 FTP_Server示例
第十七章 W55MH32 ARP示例
第十六章 W55MH32 PING示例
第十五章 W55MH32 SNMP示例
第十四章 W55MH32 TFTP示例
第十二章 W55MH32 NetBIOS示例
第十一章 W55MH32 SMTP示例
第十章 W55MH32 SNTP示例
第九章 W55MH32 HTTP Server示例
第五章 W55MH32 UDP示例
第三章 W55MH32 TCP Client示例
第二章 W55MH32 DHCP示例
WIZnet W55MH32以太網(wǎng)單片機(jī)開(kāi)發(fā)教程 第十一章 通用定時(shí)器(上篇)

第十三章 W55MH32 UPnP端口轉(zhuǎn)發(fā)示例
評(píng)論