前言
????????上一章我們用W6100-EVB-PICO開發板做TCP 客戶端連接服務器進行數據回環測試,那么本章將用開發板做TCP服務器來進行數據回環測試。
TCP是什么?什么是TCP Server?能干什么?
????????TCP (Transmission Control Protocol) 是一種面向連接的、可靠的、基于字節流的傳輸協議,用于在計算機網絡上傳輸數據。TCP Server是指TCP網絡服務的服務器端連接,用于接收客戶端的連接請求并建立連接,實現網絡數據的交互。
????????TCP Server的主要作用是監聽客戶端的連接請求,并建立與管理連接,實現數據的可靠傳輸。通過TCPServer,多個客戶端可以同時與服務器建立連接,實現數據的多點傳輸。
????????在TCP Server中,服務器程序需要指定監聽的端口號,并使用TCP協議與客戶端建立連接。一旦有客戶端連接進來,服務器程序就會為每個客戶端建立一個單獨的連接,并通過數據流對象 (NetworkStream) 與客廣端進行數據交互。
????????因此,TCP Server可以幫助設備實現多點數據交互,是設備聯網通信的重要方式之一。在工業自動化、物聯網、智能家居等應用中,TCP Server被廣泛使用。
連接方式
使開發板和我們的電腦處于同一網段:
- 開發板(設備)通過交叉線直連主機(PC)
- ?開發板和主機都接在路由器LAN口
測試工具
- 網絡調試工具(任意)
- wireshark抓包工具
回環測試
1.相關代碼
如下所示,tcp服務端的回環測試函數需要我們傳入三個參數:socket端口號、收發數據的緩存和端口,與做tcp客戶端實現思路一樣(可參考上一章內容),即通過Switch狀態機輪詢socket狀態進行相應處理,不同的是在初始化socket端口后不再是連接服務器,而是開啟端口監聽。
因為W6100這款以太網芯片支持IPv6,因此在回環模式上有著不同選擇,相應地處理上也會根據模式分別進行處理,這里選擇IPv4模式進行回環測試。
int32_t loopback_tcps(uint8_t sn, uint8_t* buf, uint16_t port, uint8_t loopback_mode)
{int32_t ret;datasize_t sentsize=0;int8_t status,inter;uint8_t tmp = 0;datasize_t received_size;uint8_t arg_tmp8;uint8_t* mode_msg;uint8_t dip[16];uint16_t dport;if(loopback_mode == AS_IPV4){mode_msg = msg_v4;}else if(loopback_mode == AS_IPV6){mode_msg = msg_v6;}else{mode_msg = msg_dual;}#ifdef _LOOPBACK_DEBUG_uint8_t dst_ip[16], ext_status;uint16_t dst_port;#endifgetsockopt(sn, SO_STATUS, &status);switch(status){case SOCK_ESTABLISHED :ctlsocket(sn,CS_GET_INTERRUPT,&inter);if(inter & Sn_IR_CON){#ifdef _LOOPBACK_DEBUG_getsockopt(sn,SO_DESTIP,dst_ip);getsockopt(sn,SO_EXTSTATUS, &ext_status);if(ext_status & TCPSOCK_MODE){//IPv6printf("%d:Peer IP : %04X:%04X", sn, ((uint16_t)dst_ip[0] << 8) | ((uint16_t)dst_ip[1]),((uint16_t)dst_ip[2] << 8) | ((uint16_t)dst_ip[3]));printf(":%04X:%04X", ((uint16_t)dst_ip[4] << 8) | ((uint16_t)dst_ip[5]),((uint16_t)dst_ip[6] << 8) | ((uint16_t)dst_ip[7]));printf(":%04X:%04X", ((uint16_t)dst_ip[8] << 8) | ((uint16_t)dst_ip[9]),((uint16_t)dst_ip[10] << 8) | ((uint16_t)dst_ip[11]));printf(":%04X:%04X, ", ((uint16_t)dst_ip[12] << 8) | ((uint16_t)dst_ip[13]),((uint16_t)dst_ip[14] << 8) | ((uint16_t)dst_ip[15]));}else{//IPv4//getSn_DIPR(sn,dst_ip);printf("%d:Peer IP : %.3d.%.3d.%.3d.%.3d, ",sn, dst_ip[0], dst_ip[1], dst_ip[2], dst_ip[3]);}getsockopt(sn,SO_DESTPORT,&dst_port);printf("Peer Port : %d\r\n", dst_port);#endifarg_tmp8 = Sn_IR_CON;ctlsocket(sn,CS_CLR_INTERRUPT,&arg_tmp8);}getsockopt(sn,SO_RECVBUF,&received_size);if(received_size > 0){if(received_size > DATA_BUF_SIZE) received_size = DATA_BUF_SIZE;ret = recv(sn, buf, received_size);getsockopt(sn,SO_DESTIP,dip);getsockopt(sn,SO_DESTPORT,&dport);printf("from the client with ip [%d.%d.%d.%d] - port [%d].\n",dip[0],dip[1],dip[2],dip[3],dport);printf("recv: %s",buf);if(ret <= 0) return ret; // check SOCKERR_BUSY & SOCKERR_XXX. For showing the occurrence of SOCKERR_BUSY.received_size = (uint16_t) ret;sentsize = 0;while(received_size != sentsize){ret = send(sn, buf+sentsize, received_size-sentsize);if(ret < 0){close(sn);return ret;}sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.}}break;case SOCK_CLOSE_WAIT :#ifdef _LOOPBACK_DEBUG_printf("%d:CloseWait\r\n",sn);#endifgetsockopt(sn, SO_RECVBUF, &received_size);if(received_size > 0) // Don't need to check SOCKERR_BUSY because it doesn't not occur.{if(received_size > DATA_BUF_SIZE) received_size = DATA_BUF_SIZE;ret = recv(sn, buf, received_size);if(ret <= 0) return ret; // check SOCKERR_BUSY & SOCKERR_XXX. For showing the occurrence of SOCKERR_BUSY.received_size = (uint16_t) ret;sentsize = 0;while(received_size != sentsize){ret = send(sn, buf+sentsize, received_size-sentsize);if(ret < 0){close(sn);return ret;}sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.}}if((ret = disconnect(sn)) != SOCK_OK) return ret;#ifdef _LOOPBACK_DEBUG_printf("%d:Socket Closed\r\n", sn);#endifbreak;case SOCK_INIT :if( (ret = listen(sn)) != SOCK_OK) return ret;#ifdef _LOOPBACK_DEBUG_printf("%d:Listen, TCP server loopback, port [%d] as %s\r\n", sn, port, mode_msg);#endifprintf("%d:Listen, TCP server loopback, port [%d] as %s\r\n", sn, port, mode_msg);break;case SOCK_CLOSED:#ifdef _LOOPBACK_DEBUG_printf("%d:TCP server loopback start\r\n",sn);#endifswitch(loopback_mode){case AS_IPV4:tmp = socket(sn, Sn_MR_TCP4, port, SOCK_IO_NONBLOCK);break;case AS_IPV6:tmp = socket(sn, Sn_MR_TCP6, port, SOCK_IO_NONBLOCK);break;case AS_IPDUAL:tmp = socket(sn, Sn_MR_TCPD, port, SOCK_IO_NONBLOCK);break;default:break;}if(tmp != sn) /* reinitialize the socket */{#ifdef _LOOPBACK_DEBUG_printf("%d : Fail to create socket.\r\n",sn);#endifreturn SOCKERR_SOCKNUM;}#ifdef _LOOPBACK_DEBUG_printf("%d:Socket opened[%d]\r\n",sn, getSn_SR(sn));sock_state[sn] = 1;#endifbreak;default:break;}return 1;
}
主函數就比較簡單,在此之前我們先聲明socket端口號和所用最大的緩存大小,不做分片處理默認為2KB;然后初始化網絡信息、目標IP地址和目標端口,最后在while循環里調用loopback_tcps并傳入相應參數即可。
注意:這里的本地端口選擇盡量避免使用特殊端口,這里使用8080;如下所示。
#define SOCKET_ID 0
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)void network_init(void);wiz_NetInfo net_info = {.mac = {0x00, 0x08, 0xdc, 0x16, 0xed, 0x2e},.ip = {192, 168, 1, 10},.sn = {255, 255, 255, 0},.gw = {192, 168, 1, 1},.dns = {8, 8, 8, 8},.ipmode = NETINFO_STATIC_V4};
wiz_NetInfo get_info;
static uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0,};
static uint16_t local_port = 8080;int main()
{ stdio_init_all();sleep_ms(2000);network_init();while(true){loopback_tcps(SOCKET_ID, ethernet_buf, local_port, AS_IPV4);sleep_ms(500);}}void network_init(void)
{uint8_t temp;wizchip_initialize();printf("W6100 tcp server example.\r\n");sleep_ms(2000);/* Determine the network lock register status */if(!ctlwizchip(SYS_NET_LOCK, &temp)){ printf("unlock.\n");NETUNLOCK();}wizchip_setnetinfo(&net_info);print_net_info(&get_info);sleep_ms(2000);
}
2.測試現象
我們編譯燒錄后,打開串行監視器,可以看到我們開發板通過串口回顯的網絡配置信息,然后我們打開網絡調試工具,配置為TCP Client模式;遠程IP地址和遠程端口為我們開發板的本地IP和端口,然后點擊連接,并發送數據測試;可以看到串口打印的信息,我們電腦作為客戶端成功連接并收到開發板回傳的數據。
我們也可以在打開wireshark抓包工具,輸入命令<ip.addr == 192.168.1.10 and tcp>過濾數據包(IP地址改成自己電腦的IP或者開發板的IP地址即可);我這里先關閉網絡調試助手,然后又打開,接著發送0~9十個阿拉伯數字,可以通過抓包工具十分清楚明了的看到具體交互過程,如下圖所示。
?相關鏈接:
本章相關例程鏈接https://gitee.com/wiznet-hk/example-of-w6100-evb-pico.gitwireshark抓包工具下載鏈接
https://www.wireshark.org/download.html