前面介紹了基于Socket方式的以太網(wǎng)通訊,接下來給大家介紹基于TCP包的通訊。內(nèi)容分為基于MM32F3270以太網(wǎng)Client的使用與基于MM32F3270以太網(wǎng)Server的使用。
首先,對TCP有個簡單的介紹:
TCP是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議。即客戶端和服務器之間在交換數(shù)據(jù)之前會先建立一個TCP連接,才能相互傳輸數(shù)據(jù)。并且提供超時重發(fā),丟棄重復數(shù)據(jù),檢驗數(shù)據(jù),流量控制等功能,保證數(shù)據(jù)能從一端傳到另一端。
TCP優(yōu)點:可靠、穩(wěn)定,TCP的可靠體現(xiàn)在TCP在傳遞數(shù)據(jù)之前,會有三次握手來建立連接,而且在數(shù)據(jù)傳遞時,有確認、窗口、重傳、擁塞控制機制,在數(shù)據(jù)傳完后,還會斷開連接用來節(jié)約系統(tǒng)資源。
TCP的缺點:慢,效率低,占用系統(tǒng)資源高,易被攻擊,TCP在傳遞數(shù)據(jù)之前,要先建連接,這會消耗時間,而且在數(shù)據(jù)傳遞時,確認機制、重傳機制、擁塞控制機制等都會消耗大量的時間,而且要在每臺設備上維護所有的傳輸連接,事實上,每個連接都會占用系統(tǒng)的CPU、內(nèi)存等硬件資源。由于TCP存在確認機制和三次握手機制,這些是導致TCP容易被人利用,實現(xiàn)DOS、DDOS、CC等攻擊。
接下來,介紹Client 的使用實現(xiàn):
Demo使用MB-039開發(fā)板,在工程中使用LwIP+FreeRTOS,實驗展示如何制作一個Client端,并發(fā)送數(shù)據(jù),實驗使用到的硬件如下:
如圖是MB-039(完整原理圖可以通過MM32官網(wǎng)下載)的ETH部分。
各個信號引腳對應如下:
在進行Client實驗前,我們先了解需要使用到的API:
1)netconn_new () 2)netconn_connect () 3)netconn_write ()
每一個函數(shù)實現(xiàn)的功能:
01、netconn_new ()
netconn_new的功能為創(chuàng)建一個新的連接結(jié)構(gòu),結(jié)構(gòu)類型可以為TCP/UDP其源碼如下:
struct netconn* netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback) { struct netconn* conn; API_MSG_VAR_DECLARE(msg); API_MSG_VAR_ALLOC_RETURN_NULL(msg); conn = netconn_alloc(t, callback); if (conn != NULL) { err_t err; API_MSG_VAR_REF(msg).msg.n.proto = proto; API_MSG_VAR_REF(msg).conn = conn; err = netconn_apimsg(lwip_netconn_do_newconn, API_MSG_VAR_REF(msg)); if (err != ERR_OK) { LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL); LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid( conn->recvmbox)); #if LWIP_TCP LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid( conn->acceptmbox)); #endif /* LWIP_TCP */ #if !LWIP_NETCONN_SEM_PER_THREAD LWIP_ASSERT("conn has no op_completed", sys_sem_valid( conn->op_completed)); sys_sem_free( conn->op_completed); #endif /* !LWIP_NETCONN_SEM_PER_THREAD */ sys_mbox_free( conn->recvmbox); memp_free(MEMP_NETCONN, conn); API_MSG_VAR_FREE(msg); return NULL; } } API_MSG_VAR_FREE(msg); return conn; }
從源碼中可以看出,其功能為申請并初始化一個netconn結(jié)構(gòu)體,同時在netconn_alloc函數(shù)中為conn變量創(chuàng)建一個接收郵箱(recvmbox),和一個信號量(conn->op_completed)。內(nèi)存申請成功后使用netconn_apimsg函數(shù)構(gòu)建一個消息,使用OS的系統(tǒng)郵箱發(fā)送給內(nèi)核,請求以太網(wǎng)協(xié)議棧去執(zhí)行l(wèi)wip_netconn_do_newconn()函數(shù),在執(zhí)行時使用conn->op_completed進行信號量同步,任務處理完成后,釋放一個信號量表示任務完成。
02、netconn_connect ()
netconn_connect作用為建立連接,在調(diào)用時將服務器端IP地址、端口號和本地的netconn結(jié)構(gòu)綁定,源碼如下:
err_t netconn_connect(struct netconn* conn, const ip_addr_t* addr, u16_t port) { API_MSG_VAR_DECLARE(msg); err_t err; LWIP_ERROR("netconn_connect: 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 */ 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_connect, API_MSG_VAR_REF(msg)); API_MSG_VAR_FREE(msg); return err; }
從源碼中可以看出,其功能為使用netconn_apimsg創(chuàng)建一個消息,通過執(zhí)行l(wèi)wip_netconn_do_connect進行信號量的同步,將addr、port與conn進行綁定。
03、netconn_write ()
netconn_write()為處于穩(wěn)定狀態(tài)的TCP協(xié)議發(fā)送數(shù)據(jù)。TCP協(xié)議數(shù)據(jù)以數(shù)據(jù)流的方式傳遞,因此只需要知道地址、長度及需要發(fā)送的數(shù)據(jù)即可,其實際函數(shù)為netconn_write_vectors_partly(源碼較長,就不貼出來了)。重點關注一下官方API文檔對于apiflags參數(shù)的介紹:
* @param apiflags combination of following flags : * - NETCONN_COPY: data will be copied into memory belonging to the stack * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent * - NETCONN_DONTBLOCK: only write the data if all data can be written at once
apiflags的值為NETCONN_COPY時,dataptr指針指向的數(shù)據(jù)將會被拷貝到為這些數(shù)據(jù)分配的內(nèi)部緩沖區(qū),在調(diào)用本函數(shù)之后可以直接對這些數(shù)據(jù)進行修改而不會影響數(shù)據(jù),但是拷貝的過程是需要消耗系統(tǒng)資源的,CPU需要參與數(shù)據(jù)的拷貝,而且還會占用新的內(nèi)存空間。
apiflags值為NETCONN_NOCOPY時,數(shù)據(jù)不會被拷貝而是直接使用dataptr指針來引用。但是這些數(shù)據(jù)在函數(shù)調(diào)用后不能立即被修改,因為這些數(shù)據(jù)可能會被放在當前TCP連接的重傳隊列中,以防對方未收到數(shù)據(jù)進行重傳,而這段時間是不確定的。但是如果用戶需要發(fā)送的數(shù)據(jù)在ROM中(靜態(tài)數(shù)據(jù)),這樣子就無需拷貝數(shù)據(jù),直接引用數(shù)據(jù)即可。
apiflags值為NETCONN_MORE時,那么接收端在組裝這些TCP報文段的時候,會將報文段首部的PSH標志置一,這些數(shù)據(jù)完成組裝的時候,將會被立即遞交給上層應用。
apiflags值為NETCONN_DONTBLOCK時,表示在內(nèi)核發(fā)送緩沖區(qū)滿的時候,再調(diào)用netconn_write()函數(shù)將不會被阻塞,而是會直接返回一個錯誤代碼ERR_VAL告訴應用程序發(fā)送數(shù)據(jù)失敗,應用程序可以自行處理這些數(shù)據(jù),在適當?shù)臅r候進行重傳操作。
apiflags值為NETCONN_NOAUTORCVD時,表示在TCP協(xié)議接收到數(shù)據(jù)的時候,調(diào)用netconn_recv_data_tcp()函數(shù)的時候不會去更新接收窗口,只能由用戶自己調(diào)用netconn_tcp_recvd()函數(shù)完成接收窗口的更新操作。
了解了以上3個API,我們開始創(chuàng)建Client工程:
static void client(void* thread_param) { struct netconn* conn; int ret; ip4_addr_t ipaddr; uint8_t send_buf[] = "This is MM32F3270 TCP Client Demon"; //(1) while(1) { conn = netconn_new(NETCONN_TCP); //(2) if (conn == NULL) { // (3) printf("create conn failed!n"); vTaskDelay(10); continue; } IP4_ADDR( ipaddr, DEST_IP_ADDR0, DEST_IP_ADDR1, DEST_IP_ADDR2, DEST_IP_ADDR3); // (4) ret = netconn_connect(conn, ipaddr, DEST_PORT); // (5) if (ret == -1) { printf("Connect failed!n"); netconn_close(conn); vTaskDelay(10); continue; } while (1) { ret = netconn_write(conn, send_buf, sizeof(send_buf), 0); // (6) vTaskDelay(1000); } } }
1)將需要發(fā)送的數(shù)據(jù)裝填進send_buf中
2)申請一個內(nèi)存區(qū)域,類型為TCP
3)如果conn為空表示申請內(nèi)存失敗
4)將地址賦值給ipaddr
5)創(chuàng)建連接,如果失敗則刪除conn
6)執(zhí)行數(shù)據(jù)發(fā)送
到這里已經(jīng)完成了工程的創(chuàng)建,但是還有一步比較重要的,配置我們的IP,將數(shù)據(jù)發(fā)送給服務器端,則需要知道服務器的地址。打開命令行窗口輸入:ipconfig
PC地址為:192.168.105.34,在sys_arch.h文件中對DEST_IP_ADDR0 、DEST_IP_ADDR1、DEST_IP_ADDR2、DEST_IP_ADDR3進行修改,DEST_PORT隨意修改,值得注意的同一個設備是如果創(chuàng)建多個網(wǎng)卡,PORT成不同的值即可,后面我們會進行這類實驗,設備IP需要設置在同一個網(wǎng)段內(nèi)通信才能進行IP_ADDR0、IP_ADDR1、IP_ADDR2,需要與PC地址保持一致,IP_ADDR3可以隨意設置(和PC地址不一致即可)。
#define DEST_IP_ADDR0 192 #define DEST_IP_ADDR1 168 #define DEST_IP_ADDR2 105 #define DEST_IP_ADDR3 34 #define DEST_PORT 5001 #define IP_ADDR0 192 #define IP_ADDR1 168 #define IP_ADDR2 105 #define IP_ADDR3 137
將程序下載入開發(fā)板中,使用SSCOM工具進行如下設置:
點擊偵聽:
可以正常偵聽并接收到數(shù)據(jù),表明實驗成功。實驗程序請登錄我們的官網(wǎng)下載MM32F3270 SDK,工程路徑如下:
~MM32F3270_Lib_Samples_V0.90Demo_appEthernet_DemoETH_RTOSFreertos_Client
來源:靈動MM32MCU
審核編輯:湯梓紅
-
以太網(wǎng)
+關注
關注
40文章
5343瀏覽量
170797 -
Socket
+關注
關注
0文章
196瀏覽量
34621 -
TCP
+關注
關注
8文章
1347瀏覽量
78933 -
FreeRTOS
+關注
關注
12文章
483瀏覽量
61915
發(fā)布評論請先 登錄
相關推薦
評論