<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

基于LwIP的TCP客戶端設計

CHANBAEK ? 來源:木南創智 ? 作者:尹家軍 ? 2022-12-14 15:12 ? 次閱讀

上一篇我們基于LwIP協議棧的RAW API實現了一個TCP服務器的簡單應用,接下來一節我們來實現一個TCP客戶端的簡單應用。

1 、 TCP****簡述

TCP(Transmission Control Protocol 傳輸控制協議)是一種面向連接的、可靠的、基于字節流的傳輸層通信協議,由IETF的RFC 793定義。在簡化的計算機網絡OSI模型中,它完成第四層傳輸層所指定的功能,與用戶數據報協議(UDP)是同一層內的,另一個重要的傳輸協議。在因特網協議族(Internet protocol suite)中,TCP層是位于IP層之上,應用層之下的中間層。不同主機的應用層之間經常需要可靠的、像管道一樣的連接,但是IP層本身不提供這樣的流機制,而是提供不可靠的包交換,恰好TCP協議不足了這一應用需求。

應用層向TCP層發送用于網間傳輸的、用8位字節表示的數據流,然后TCP把數據流分區成適當長度的報文段。之后TCP把結果包傳給IP層,由它來通過網絡將包傳送給接收端實體的TCP層。TCP為了保證不發生丟包,就給每個包一個序號,同時序號也保證了傳送到接收端實體的包的按序接收。然后接收端實體對已成功收到的包發回一個相應的確認(ACK);如果發送端實體在合理的往返時延(RTT)內未收到確認,那么對應的數據包就被假設為已丟失將會被進行重傳。TCP用一個校驗和函數來檢驗數據是否有錯誤;在發送和接收時都要計算校驗和,以確保數據的正確性。TCP協議的數據包結構如下:

TCP數據包中各部分的含義如下:

1 )源端口和目標端口

源端口和目標端口各占2個字節。用來告知主機該報文段是來自哪里以及傳送給哪里。進行 TCP 通訊時,客戶端通常使用系統自動選擇的臨時端口號,而服務器則根據應用不同使用知名服務端口號。

2 )序列號

序列號占4個字節。 TCP是面向字節流的,在一個 TCP 連接中傳輸的字節流中的每個字節都按照順序編號。 由于序列號由32位表示,所以最大值為2的32次方,序號增加到最大值的時候,下一個序號又回到了0。也就是說 TCP 協議可對 4GB 的數據進行編號,在一般情況下可保證當序號重復使用時,舊序號的數據早已經通過網絡到達終點或者丟失了。

3 )確認號

確認號也是占4個字節。表示期望收到對方下一個報文段的序號值。 表明該序號之前的所有數據已經正確無誤的收到。確認號只有當ACK標志為1時才有效。

4 TCP****首部長度

TCP首部長度也稱為數據偏移占半個字節 (4 位)。 它指出了 TCP報文段的數據起始處距離TCP報文的起始處有多遠。當了解了LwIP中TCP存儲數據結構后,會發現這個值是很有用的。

5 TCP****標志位

TCP標志位,一共有 6 個,分別占 1 位,共 6 位 。每一位的值只有0和 1,分別表達不同意思。

  • URG 標志 ,稱為緊急標志,當URG=1的時候,表示緊急指針有效。它告訴系統此報文段中有緊急數據,應盡快傳送,而不要按原來的排隊順序來傳送。URG標志要與首部中的“緊急指針”字段配合使用。
  • ACK 標志 ,稱為確認標志,當ACK=1的時候,確認號有效。一般稱帶有ACK標志的TCP報文段為“確認報文段”。TCP規定,在連接建立后所有傳送的報文段都必須把ACK設置為1。
  • PSH 標志 ,稱為推送標志,當PSH = 1的時候,表示該報文段高優先級,接收方TCP應該盡快推送給接收應用程序,而不用等到整個TCP緩存都填滿了后再交付。
  • RST 標志 ,稱為復位標志,當RST =1的時候,表示TCP連接中出現嚴重錯誤,需要釋放并重新建立連接。一般稱攜帶RST標志的TCP報文段為“復位報文段”。
  • SYN 標志 ,稱為同步標志,當SYN = 1的時候,表明這是一個請求連接報文段。一般稱攜帶SYN標志的TCP報文段為“同步報文段”。在TCP 三次握手中的第一個報文就是同步報文段,在連接建立時用來同步序號。 對方若同意建立連接,則應在響應的報文段中使SYN = 1和ACK = 1。
  • FIN 標志 ,稱為終止標志,當FIN = 1時,表示此報文段的發送方的數據已經發送完畢,并要求釋放TCP連接。 一般稱攜帶FIN的報文段為“結束報文段”。在TCP四次揮手釋放連接的時候,就會用到該標志。

6 )窗口大小

窗口大小占2字節。該字段明確指出了現在允許對方發送的數據量,它告訴對方本端的TCP接收緩沖區還能容納多少字節的數據,這樣對方就可以控制發送數據的速度。窗口大小的值是指,從本報文段首部中的確認號算起,接收方目前允許對方發送的數據量。

7 )校驗和

校驗和占2個字節。由發送端填充,接收端對 TCP 報文段執行 CRC 算法,以檢驗 TCP 報文段在傳輸過程中是否損壞,如果損壞這丟棄。檢驗范圍包括首部和數據兩部分,這也是 TCP 可靠傳輸的一個重要保障。

8 )緊急指針

緊急指針占2個字節。僅在URG=1時才有意義,它指出本報文段中的緊急數據的字節數。 當URG = 1時,發送方TCP就把緊急數據插入到本報文段數據的最前面,而在緊急數據后面的數據仍是普通數據。因此,緊急指針指出了緊急數據的末尾在報文段中的位置。

2 、 TCP****客戶端設計

我們已經對TCP協議及其報文格式做了簡單說明,接下來我們將結合LwIP協議棧,使用RAW API實現一個TCP客戶端的簡單應用。

2.1 、 TCP相關的RAW API****函數

在開始實現TCP服務器之前,我們首先來看一看LwIP中與TCP相關的RAW API函數有哪些。并簡單的了解一下其功能。

1 )、建立TCP連接的API函數:

2 )、發送TCP數據的API函數:

3 )、接收TCP數據的API函數:

4 )、 TCP輪詢API****函數:

5 )、關閉和中止TCP連接的API函數:

2.2 、 TCP****客戶端的工作流程

我們已經了解了LwIP中實現TCP的RAW API函數,也有了實現TCP服務器的經驗,現在我們來實現一個客戶端操作??蛻舳说墓ぷ髁鞒涛覀兒唵蚊枋鋈缦拢?/p>

1 )、新建控制快

使用tcp_new()函數建立一個TCP控制塊。

2 )、綁定控制塊

對于客戶端來說,并不需要顯性的調用tcp_bind函數來為其綁定IP和端口,因為在客戶端向服務器發起連接時,LwIP內核會自動為客戶端控制塊綁定一個端口。但如果用戶確實想顯示使用也沒有問題。

3 )、建立連接

對于客戶端程序來說,它需要主動發起會話,應為服務器一直在等待中,所以客戶端需要向服務器發送一個SYN握手報文。這一過程使用tcp_connect函數來完成。同時會注冊一個連接完成回調函數,因為在連接建立后,內核就會調用這個函數。

4 )、發送請求

使用tcp_write函數發送一個數據通訊請求,當然要以服務器能夠理解的形式。其實就是告訴服務器,客戶端有什么想要做的,然后等待服務器的反饋。

5 )、接收數據并處理

一旦連接成功,connect完成回調函數會調用tcp_recv函數注冊一個接收完成的處理函數。對于客戶端來說,接收到服務器返回的數據,就會調用這一回調函數進行處理。然后其處理過程與服務器類似:接收到數據后,首先通知更新接受窗口(使用tcp_recved函數),處理并發送數據(使用tcp_write函數),數據發送成功則清除已發送的數據(使用tcp_sent函數),最后關閉連接(使用函數tcp_close)。

用流程圖表述如下:

在上述流程圖中我們列出了每一環節所用到的主要函數,其他一些函數用到了但未列出,有興趣可以免查閱源碼或者看相關的手冊。

2.3 、常用端口

TCP所使用的端口有很多與UDP是相同的,也有一些不一樣。為了方便操作我們已經將常用的端口以宏定義的形式存儲在一個文件中?,F將常用的端口列于下,我們也是使用下列端口來實現我們的操作。

對于端口這塊奇石前面已經描述過了,在這里只是簡單的說一下,因為我們實現的功能比較簡單,依然使用TCP回環協議端口。

3 、 TCP****客戶端實現

經過上述的分析以及我們前面實現TCP服務器的經驗,實現TCP客戶端已經沒有問題。我們將TCP客戶端分成4個函數來實現。首先依然是實現TCP客戶端的初始化:

1 /* TCP客戶端初始化 */
 2 void Tcp_Client_Initialization(void)
 3 {
 4   struct tcp_pcb *tcp_client_pcb;
 5   ip_addr_t ipaddr;
 6  
 7   /* 將目標服務器的IP寫入一個結構體,為pc機本地連接IP地址 */
 8   IP4_ADDR(&ipaddr,serverIP[0],serverIP[1],serverIP[2],serverIP[3]);
 9  
10   /* 為tcp客戶端分配一個tcp_pcb結構體    */
11   tcp_client_pcb = tcp_new();
12  
13   /* 綁定本地端號和IP地址 */
14   tcp_bind(tcp_client_pcb, IP_ADDR_ANY, TCP_CLIENT_PORT);
15  
16   if (tcp_client_pcb != NULL)
17   {
18     /* 與目標服務器進行連接,參數包括了目標端口和目標IP */
19     tcp_connect(tcp_client_pcb, &ipaddr, TCP_SERVER_PORT, TCPClientConnected);
20    
21     tcp_err(tcp_client_pcb, TCPClientConnectError);
22   }
23 }

上述初始化的代碼很簡單,有兩個地方需要說一下:一是使用tcp_connect注冊連接完成的處理回調函數;二是使用tcp_err注冊了連接錯誤處理回調函數。很明顯接下來我們需要實現這兩個函數。

連接到服務器成功后的回調函數是tcp_connected_fn類型。在客戶端建立一個連接后,內核會調用這個函數。在這個函數中,客戶端回想服務器發送最初的操作請求,并且會在這個函數中注冊數據接收處理回調函數。

1 /* TCP客戶端連接到服務器回調函數 */
 2 static err_t TCPClientConnected(void *arg, struct tcp_pcb *pcb, err_t err)
 3 {
 4   char clientString[]="This is a new client connection.\\r\\n";
 5  
 6   /* 配置接收回調函數 */
 7   tcp_recv(pcb, TCPClientCallback);
 8  
 9   /* 發送一個建立連接的問候字符串*/
10   tcp_write(pcb,clientString, strlen(clientString),0);
11  
12   return ERR_OK;
13 }

對于TCP客戶端連接服務器錯誤回調函數,它是tcp_err_fn類型,在這個程序中主要完成連接異常結束時的一些處理,可以釋放一些必要的資源。在這個函數被內核調用時,連接實際上已經斷開,相關控制塊也已經被刪除。所以在這個函數中我們可以重新初始化連接及其資源。在這里額我們就是使用它來重新初始化TCP客戶端。

1 /* TCP客戶端連接服務器錯誤回調函數 */
2 static void TCPClientConnectError(void *arg, err_t err)
3 {
4   /* 重新啟動連接 */
5   Tcp_Client_Initialization();
6 }

最后我們需要實現的是TCP客戶端接收到數據后的數據處理回調函數。這個函數其實就是我們前面連接成功時,注冊過的TCP客戶端數據接收處理函數。這個函數是tcp_recv_fn類型。這是使用RAW API實現TCP客戶端功能最重要的一個函數,因為它決定TCP客戶端的具體功能。

1 /* TCP客戶端接收到數據后的數據處理回調函數 */
 2 static err_t TCPClientCallback(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err)
 3 {
 4   struct pbuf *tcp_send_pbuf;
 5   char echoString[]="This is the server content echo:\\r\\n";
 6  
 7   if (tcp_recv_pbuf != NULL)
 8   {
 9     /* 更新接收窗口 */
10     tcp_recved(pcb, tcp_recv_pbuf->tot_len);
11  
12     /* 將接收到的服務器內容回顯*/
13     tcp_write(pcb,echoString, strlen(echoString), 1);
14     tcp_send_pbuf = tcp_recv_pbuf;
15     tcp_write(pcb, tcp_send_pbuf->payload, tcp_send_pbuf->len, 1);
16  
17     pbuf_free(tcp_recv_pbuf);
18   }
19   else if (err == ERR_OK)
20   {
21     tcp_close(pcb);
22     Tcp_Client_Initialization();
23  
24     return ERR_OK;
25   }
26  
27   return ERR_OK;
28 }

到這里,我們就實現了一個簡單的TCP客戶端。對于TCP客戶端的具體功能就在于就收處理回調函數的實現了。具體的應用只是功能上的復雜程度不一樣,結構上是一樣的。

4 、結論

本篇我們基于LwIP實現了簡單的TCP客戶端應用。通過回調函數的實現方式,整個過程與TCP服務器的實現基本類似。我們用設計的TCP客戶端去連接TCP服務器應用,測試連接都沒有問題。當然,我們可以在此基礎上設計更復雜的應用層協議實現我們想要的功能,只需要在回調函數中添加處理就可以了。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 服務器
    +關注

    關注

    12

    文章

    8271

    瀏覽量

    83062
  • TCP
    TCP
    +關注

    關注

    8

    文章

    1282

    瀏覽量

    78445
  • 客戶端
    +關注

    關注

    1

    文章

    284

    瀏覽量

    16445
  • LwIP
    +關注

    關注

    1

    文章

    82

    瀏覽量

    26719
收藏 人收藏

    評論

    相關推薦

    LWIPTCP客戶端測試前兩個字節不顯示還出現延遲

    _abort(tpcb);//終止連接,刪除pcb控制塊ret_err=ERR_ABRT;}return ret_err;} 我把整個工程發上來。大神給看看啊 TCP客戶端無DHCP版.rar (7.88 MB )
    發表于 07-04 04:35

    lwipTCP客戶端返回不了數據該怎么辦?

    err = netconn_connect(tcp_clientconn,&server_ipaddr,9100);這句話好像是阻塞的,所以返回不了數據啊,我的現象是程序開機前,先建立服務器就沒問題,如果開機以后再建立服務器,就連接不上,而且程序一直卡在這,想重連都不行
    發表于 07-16 04:35

    為什么lwip tcp客戶端無法連接?

    如題,用的戰艦開發板,原子提供的例子,ping得通,但是網絡助手提示沒有連接,提示如下:
    發表于 08-21 21:55

    為什么LWIP TCP客戶端測試中總是出現數據掉包和數據重發現象?

    參考發燒友網絡實驗中的TCP CLIENT測試移植的程序,將STM32作為客戶端,電腦作為服務器,STM32連續發送數據給服務器時,發送一定次數的數據后數據就發不上去了,在服務器用抓包軟件看,發現是數據掉包和數據重發的問題,請
    發表于 08-30 04:36

    為什么STM32F746G-Discovery板lwip tcp客戶端無法建立連接?

    ,但是并未成功,這個例程原本是一個基于tcp連接的一個http服務器,然后我改了里面的http_server_netconn_thread函數參考f7教程里的tcp客戶端例程修改了,發現一只卡在
    發表于 09-25 00:40

    LWIP TCP客戶端硬件出現以下錯誤該怎么辦?

    將《網絡實驗8 NETCONN_TCP 客戶端》Demo移植到項目中后,LWIP TCP客戶端 發送消息的時候,產生hardFault中
    發表于 10-27 23:12

    為什么LWIPTCP客戶端服務器斷開后繼續發送數據就無法檢測到連接狀態?

    發現LWIPTCP客戶端有個BUG,當服務器開之后,如果還繼續發送數據,那就不能檢測到連接狀態。求助求助
    發表于 10-29 20:26

    為什么我不能用DHCP獲取動態IP地址?

    請問一下,我用開發板源程序做帶系統的LWIP_TCP客戶端實驗時,為什么我不能用DHCP獲取動態IP地址,只能是靜態IP地址,我看DHCP的定義為1,請問該如何解決呢?(其中開發板為stm32f103)@zuozhongkai
    發表于 11-07 04:21

    為什么戰艦開發板的LWIPTCP客戶端實驗不能和PC連接在一起?

    探索者開發板的LWIP擴展例程中的網絡實驗4 RAW_TCP客戶端實驗和網絡實驗8 NETCONN_TCP 客戶端例程為什么不能和PC連接在
    發表于 11-06 22:03

    使用LwIPtcp客戶端tcp服務器都結合起來使用就卡死了

    大家好,使用LwIPtcp 客戶端tcp服務器都沒有問題,但是結合起來使用就卡死了 怎么辦? 經過調試分析是卡在了tcp_active
    發表于 03-18 04:22

    FreeRTOS+Lwip TCP客戶端只能和一個接收

    求助高手幫忙 我用F407 實現 FreeRTOS+LwIP客戶端程序現在的問題是用網絡調試助手建立TCPserver連接后 收發數據確實沒什么問題 ,但是2臺F407設備的客戶端連接后 只能
    發表于 03-19 00:58

    F407的LWIP TCP客戶端實驗連接不上

    F407,我開始用的UDP,可以連接也可以發送數據,可是TCP客戶端實驗下栽進去了無法連接,請問這個可能是什么問題呢
    發表于 03-23 22:01

    基于lwipTCP客戶端同時連接雙服務器連接不上

    的程序求大神應該怎么實現,問題出在哪兒下面是主要程序//tcp客戶端任務函數static void tcp_client_thread(void *arg){OS_CPU_SR cpu_sr;u32
    發表于 03-25 02:03

    stm32f107vc lwip tcp客戶端

    stm32f107vc lwip tcp客戶端 服務器數據傳輸第一篇TCP客戶端模式簡單數據收發 ----控制開發板LED燈概要建立
    發表于 08-06 09:17

    如何去實現stm32f107vc lwip tcp客戶端服務器的數據傳輸呢

    怎么去建立LWIP客戶端模式呢?如何去實現stm32f107vc lwip tcp客戶端服務器的數據傳輸呢?
    發表于 11-04 06:54
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>