前面重點對Client的創建方式及使用方式進行了介紹,本節通過Server實驗對TCP通信過程進行一次介紹。
在TCP/IP協議中,傳輸層及以下層的機制是由內核提供的。應用層由用戶提供,應用層程序對通信數據進行解析處理,傳輸層及以下層處理通信的細節(將數據從一端傳入另外一端)。應用層數據通過協議棧發送到網絡上時,每層協議都要增加一個數據部首(header),進行一次封裝。其中不同的協議層對數據包有不同的稱謂,在傳輸層叫段(segment),在網絡層叫做數據報(datagram),在鏈路層稱為幀(frame)。
在通信過程中,發送端執行以下動作:首先程序進行編碼,確定通信的建立連接、發送數據的時間。接著建立TCP連接,TCP根據應用指示負責建立連接、發送數據及斷開連接。TCP首部包括源端口號和目標端口號、序號和校驗和,加完首部后數據包繼續往下傳遞到IP層,IP層加上IP首部包括地址等信息用于尋址操作,之后將數據繼續往下傳遞附加數據鏈路層首部。最后發送時的分組數據包會加上以太網包尾(用于循環冗余校驗)。
主機端:收到數據包后會在以太網數據包中找到MAC地址,判斷是否為自己的數據包,如果不是則丟棄。如果是傳遞給IP層處理,以此類推,不斷往上傳遞到TCP層。在TCP層通過校驗和判斷數據是否損壞,然后檢查是否按序號接收數據,最后檢查端口號。處理完成這一切后數據包繼續往上層發送,即應用層。如果出現主機空間已滿等情況,主機則會發送“處理異常”通知發送端。
實驗使用MB-039開發板,在工程中使用LwIP+FreeRTOS,實驗展示如何制作一個TCP Server,并收發數據,實驗使用到的硬件如下:
如圖是MB-039(完整原理圖可以通過MM32官網下載)的ETH部分。
各個信號引腳對應如下:
在進行Server實驗前,我們先了解需要使用到的API:
1)netconn_bind ()
2)netconn_listen ()
3)netconn_accept ()
以下分API展開介紹:
01、netconn_bind ()
從源碼中可以看出其主要功能:為conn(服務器端)綁定地址與端口號。
err_t netconn_bind(struct netconn* conn, const ip_addr_t* addr, u16_t port) { API_MSG_VAR_DECLARE(msg); err_t err; LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;); #if LWIP_IPV4 /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */ if (addr == NULL) { addr = IP4_ADDR_ANY; } #endif /* LWIP_IPV4 */ #if LWIP_IPV4 LWIP_IPV6 if ((netconn_get_ipv6only(conn) == 0) ip_addr_cmp(addr, IP6_ADDR_ANY)) { addr = IP_ANY_TYPE; } #endif /* LWIP_IPV4 LWIP_IPV6 */ API_MSG_VAR_ALLOC(msg); API_MSG_VAR_REF(msg).conn = conn; API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr); API_MSG_VAR_REF(msg).msg.bc.port = port; err = netconn_apimsg(lwip_netconn_do_bind, API_MSG_VAR_REF(msg)); API_MSG_VAR_FREE(msg); return err; }
02、netconn_listen ()
netconn_listen指向的函數是:netconn_listen_with_backlog,作用:使服務器進入監聽狀態,等待遠端的連接請求。
err_t netconn_listen_with_backlog(struct netconn* conn, u8_t backlog) { #if LWIP_TCP API_MSG_VAR_DECLARE(msg); err_t err; /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */ LWIP_UNUSED_ARG(backlog); LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;); API_MSG_VAR_ALLOC(msg); API_MSG_VAR_REF(msg).conn = conn; #if TCP_LISTEN_BACKLOG API_MSG_VAR_REF(msg).msg.lb.backlog = backlog; #endif /* TCP_LISTEN_BACKLOG */ err = netconn_apimsg(lwip_netconn_do_listen, API_MSG_VAR_REF(msg)); API_MSG_VAR_FREE(msg); return err; #else /* LWIP_TCP */ LWIP_UNUSED_ARG(conn); LWIP_UNUSED_ARG(backlog); return ERR_ARG; #endif /* LWIP_TCP */ }
03、netconn_accept ()
netconn_accept(代碼較長,這里不進行粘貼)用于TCP服務器中,等待著遠端主機的連接請求,并且建立一個新的TCP連接,在調用這個函數之前需要通過調用 listen()函數讓服務器進入監聽狀態。accept()函數的調用會阻塞應用線程直至與遠程主機建立TCP連接。參數addr是一個返回結果參數,它的值由accept()函數設置,其實就是遠程主機的地址與端口號等信息,當新的連接已經建立后,遠端主機的信息將保存在連接句柄中,能夠標識連接對象。
了解了以上3個API,我們開始創建Server工程:
static void server(void* thread_param) { struct netconn* conn, *newconn; err_t err; LWIP_UNUSED_ARG(arg); #if LWIP_IPV6 conn = netconn_new(NETCONN_TCP_IPV6); netconn_bind(conn, IP6_ADDR_ANY, LOCAL_PORT); #else /* LWIP_IPV6 */ conn = netconn_new(NETCONN_TCP); //① netconn_bind(conn, IP_ADDR_ANY, LOCAL_PORT); //② #endif /* LWIP_IPV6 */ LWIP_ERROR("tcpecho: invalid conn", (conn != NULL), return;); printf("The local port number is%dnn", LOCAL_PORT); netconn_listen(conn); //③ while (1) { err = netconn_accept(conn, newconn); //④ if (err == ERR_OK) { struct netbuf* buf; void* data; u16_t len; while ((err = netconn_recv(newconn, buf)) == ERR_OK) { //⑤ do { netbuf_data(buf, data, len); err = netconn_write(newconn, data, len, NETCONN_COPY); //⑥ } while (netbuf_next(buf) >= 0); netbuf_delete(buf); //⑦ } netconn_close(newconn); //⑧ netconn_delete(newconn); //⑨ } } }
1、申請一個連接結構,指定參數是NETCONN_TCP,即TCP連接
2、綁定本地的IP地址與端口號
3、使TCP服務器進入監聽狀態
4、處理客戶端的連接請求,當只有當有客戶端發送連接請求的時候才會處理,否則將進入阻塞態,而客戶端的信息保存在newconn連接結構中
5、接收數據,并裝填進buf
6、對接收的數據進行轉發(指定為不拷貝方式NETCONN_COPY)
7、釋放數據空間
8、主動關閉客戶端的連接
9、釋放newconn空間
到這里已經完成了工程的創建,看一下PC的IP地址,設備需要處于同一網段,以方便測試。
打開命令行窗口輸入:ipconfig
PC的地址為:192.168.105.34,在sys_arch.h文件中對DEST_IP_ADDR0 、DEST_IP_ADDR1、DEST_IP_ADDR2、DEST_IP_ADDR3進行修改,DEST_PORT 隨意修改。
#define LOCAL_PORT 2021 #define IP_ADDR0 192 #define IP_ADDR1 168 #define IP_ADDR2 105 #define IP_ADDR3 21
將程序下載入開發板中,使用NetAssist進行如下設置:
1)協議設置,此時設備為Server,則PC為Client
2)設置遠程主機地址(即設備地址)
3)端口號
點擊連接,若提示連接失敗,則Ping一下開發板地址,可以正常Ping通則檢查端口號;如果無法Ping通則需要對工程進行檢查。
任意輸入字符進行發送。
通過上圖可以觀察到發送成功,并且設備返回數據與發送數據一致,表明實驗成功。實驗程序請登錄我們的官網下載MM32F3270 SDK,工程路徑如下:~MM32F3270_Lib_Samples_V0.90Demo_appEthernet_DemoETH_RTOSFreertos_Server。
來源:靈動MM32MCU
審核編輯:湯梓紅
-
以太網
+關注
關注
40文章
5419瀏覽量
171603 -
通信
+關注
關注
18文章
6024瀏覽量
135950 -
Server
+關注
關注
0文章
90瀏覽量
24029 -
TCP
+關注
關注
8文章
1353瀏覽量
79055
發布評論請先 登錄
相關推薦
評論