0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

LwIP中TCP協(xié)議是如何實(shí)現(xiàn)的

汽車電子技術(shù) ? 來源:物聯(lián)網(wǎng)IoT開發(fā) ? 作者: 杰杰mcu ? 2023-02-14 10:39 ? 次閱讀

前言說明

本文章代碼非常多,并且難懂,如非特別需要,否則不建議閱讀!建議學(xué)習(xí)TCP協(xié)議理論,等基礎(chǔ)扎實(shí)后再去閱讀lwip源碼,本文章的源碼只是輔助真正有需要的人閱讀!

TCP控制塊

與其他協(xié)議一樣,為了描述TCP協(xié)議,LwIP定義了一個(gè)名字叫tcp_pcb的結(jié)構(gòu)體,可以稱之為TCP控制塊,其內(nèi)定義了大量的成員變量,基本定義了整個(gè)TCP協(xié)議運(yùn)作過程的所有需要的東西,如發(fā)送窗口、接收窗口、數(shù)據(jù)緩沖區(qū)。超時(shí)處理、擁塞控制、滑動(dòng)窗口等等。

1/** TCP協(xié)議控制塊 */
  2struct tcp_pcb 
  3{
  4  IP_PCB;
  5/** 協(xié)議特定的PCB成員 */
  6  TCP_PCB_COMMON(struct tcp_pcb);
  7
  8  /* 遠(yuǎn)端端口號(hào) */
  9  u16_t remote_port;
 10
 11  tcpflags_t flags;
 12#define TF_ACK_DELAY   0x01U   /* 延遲發(fā)送ACK */
 13#define TF_ACK_NOW     0x02U   /* 立即發(fā)送ACK. */
 14#define TF_INFR        0x04U   /* 在快速恢復(fù)。 */
 15#define TF_CLOSEPEND   0x08U   /* 關(guān)閉掛起 */
 16#define TF_RXCLOSED    0x10U   /* rx由tcp_shutdown關(guān)閉 */
 17#define TF_FIN         0x20U   /* 連接在本地關(guān)閉 */
 18#define TF_NODELAY     0x40U   /* 禁用Nagle算法 */
 19#define TF_NAGLEMEMERR 0x80U   /* 本地緩沖區(qū)溢出 */
 20#define TF_TIMESTAMP   0x0400U   /* Timestamp option enabled */
 21#endif
 22#define TF_RTO         0x0800U /* RTO計(jì)時(shí)器 */
 23
 24  u8_t polltmr, pollinterval;
 25  /* 控制塊被最后一次處理的時(shí)間 */
 26  u8_t last_timer;           
 27  u32_t tmr;
 28
 29  /* 接收窗口相關(guān)的字段 */
 30  u32_t rcv_nxt;   /* 下一個(gè)期望收到的序號(hào) */
 31  tcpwnd_size_t rcv_wnd;   /* 接收窗口大小 */
 32  tcpwnd_size_t rcv_ann_wnd; /* 告訴對(duì)方窗口的大小 */
 33  u32_t rcv_ann_right_edge; /* 窗口的右邊緣 */
 34
 35  /* 重傳計(jì)時(shí)器。*/
 36  s16_t rtime;
 37
 38  u16_t mss;   /* 最大報(bào)文段大小 */
 39
 40  /* RTT(往返時(shí)間)估計(jì)變量 */
 41  u32_t rttest; /* RTT估計(jì),以為500毫秒遞增 */
 42  u32_t rtseq;  /* 用于測試RTT的報(bào)文段序號(hào) */
 43  s16_t sa, sv; /* RTT估計(jì)得到的平均值與時(shí)間差 */
 44
 45  s16_t rto;    /* 重傳超時(shí) */
 46  u8_t nrtx;    /* 重傳次數(shù) */
 47
 48  /* 快速重傳/恢復(fù) */
 49  u8_t dupacks;
 50  u32_t lastack; /* 接收到的最大確認(rèn)序號(hào) */
 51
 52  /* 擁塞避免/控制變量 */
 53  tcpwnd_size_t cwnd;     /* 連接當(dāng)前的窗口大小 */
 54  tcpwnd_size_t ssthresh; /* 擁塞避免算法啟動(dòng)的閾值 */
 55
 56
 57  u32_t rto_end;
 58
 59  u32_t snd_nxt;   /* 下一個(gè)要發(fā)送的序號(hào) */
 60  u32_t snd_wl1, snd_wl2; /* 上一次收到的序號(hào)和確認(rèn)號(hào) */
 61  u32_t snd_lbb;       /* 要緩沖的下一個(gè)字節(jié)的序列號(hào) */
 62  tcpwnd_size_t snd_wnd;   /* 發(fā)送窗口大小 */
 63  tcpwnd_size_t snd_wnd_max; /* 對(duì)方的最大發(fā)送方窗口 */
 64
 65  /* 可用的緩沖區(qū)空間(以字節(jié)為單位)。 */
 66  tcpwnd_size_t snd_buf;   
 67
 68  tcpwnd_size_t bytes_acked;
 69
 70  struct tcp_seg *unsent;   /* 未發(fā)送的報(bào)文段 */
 71  struct tcp_seg *unacked;  /* 已發(fā)送但未收到確認(rèn)的報(bào)文段 */
 72  struct tcp_seg *ooseq; 
 73  /* 以前收到但未被上層處理的數(shù)據(jù) */
 74  struct pbuf *refused_data; 
 75
 76#if LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG
 77  struct tcp_pcb_listen* listener;
 78#endif 
 79
 80//TCP協(xié)議相關(guān)的回調(diào)函數(shù)
 81#if LWIP_CALLBACK_API
 82  /* 當(dāng)數(shù)據(jù)發(fā)送成功后被調(diào)用 */
 83  tcp_sent_fn sent;
 84  /* 接收數(shù)據(jù)完成后被調(diào)用 */
 85  tcp_recv_fn recv;
 86  /* 建立連接后被調(diào)用 */
 87  tcp_connected_fn connected;
 88  /* 該函數(shù)被內(nèi)核周期調(diào)用 */
 89  tcp_poll_fn poll;
 90  /* 發(fā)送錯(cuò)誤時(shí)候被調(diào)用 */
 91  tcp_err_fn errf;
 92#endif 
 93
 94  /* 保持活性 */
 95  u32_t keep_idle;
 96  /* 堅(jiān)持計(jì)時(shí)器計(jì)數(shù)器值 */
 97  u8_t persist_cnt;
 98  u8_t persist_backoff;
 99  u8_t persist_probe;
100
101  /* 保持活性報(bào)文發(fā)送次數(shù) */
102  u8_t keep_cnt_sent;
103
104};

IP_PCB又是一個(gè)宏定義,定義了IP層需要的一些成員變量:

1#define IP_PCB                              2  /* 本地ip地址與遠(yuǎn)端IP地址 */              3  ip_addr_t local_ip;                       4  ip_addr_t remote_ip;                      5  /* 綁定netif索引 */                       6  u8_t netif_idx;                           7  /* 套接字選項(xiàng) */                          8  u8_t so_options;                          9  /* 服務(wù)類型 */                           10  u8_t tos;                                11  /* 生存時(shí)間 */                           12  u8_t ttl                                 13  /* 鏈路層地址解析提示 */                 14  IP_PCB_NETIFHINT

TCP_PCB_COMMON則是定義了一些特定的TCP控制塊的成員變量:

1#define TCP_PCB_COMMON(type) 2  type *next; /* 指向鏈表中的下一個(gè)控制塊 */ 3  void *callback_arg; 4  TCP_PCB_EXTARGS 5  enum tcp_state state; /* TCP狀態(tài) */ 6  u8_t prio; 7  /* 本地主機(jī)端口號(hào) */ 8  u16_t local_port

LwIP中除了定義了一個(gè)完整的TCP控制塊之外,還定義了一個(gè)刪減版TCP控制塊——tcp_pcb_listen,它用于描述處于監(jiān)聽狀態(tài)的TCP連接,因?yàn)榉峙渫暾?code>TCP控制塊是比較消耗內(nèi)存資源的,而TCP協(xié)議在建立連接之前是無傳輸數(shù)據(jù)的,因此在監(jiān)聽的時(shí)候只需要把建立連接的主機(jī)的相關(guān)信息得到,然后無縫切換到完整的TCP控制塊中,這樣子就能節(jié)省不少資源(畢竟在嵌入式設(shè)備中資源是有限的)。除了tcp_pcb_listen外,LwIP還定義了4個(gè)鏈表來維護(hù)TCP連接時(shí)的各種狀態(tài),分別是:

  1. 新綁定的端口鏈表,用于記錄新綁定端口的TCP控制塊。
  2. 監(jiān)聽鏈表:用于記錄處于監(jiān)聽狀態(tài)的TCP控制塊 。
  3. 活動(dòng)狀態(tài)鏈表:用于記錄處于其他(活動(dòng))狀態(tài)的TCP控制塊。
  4. TIME_WAIT鏈表:用于記錄處于TIME_WAIT狀態(tài)的控制塊。
1/** 用于監(jiān)聽的TCP協(xié)議控制塊 */
 2struct tcp_pcb_listen {
 3/** 所有PCB類型的通用成員 */
 4  IP_PCB;
 5/** 協(xié)議特定的PCB成員 */
 6  TCP_PCB_COMMON(struct tcp_pcb_listen);
 7};
 8
 9/* TCP 控制塊鏈表. */
10/** 新綁定的端口 */
11struct tcp_pcb *tcp_bound_pcbs;
12/** 處于監(jiān)聽狀態(tài)的TCP控制塊 */
13union tcp_listen_pcbs_t tcp_listen_pcbs;
14/** 穩(wěn)定的TCP連接 */
15struct tcp_pcb *tcp_active_pcbs;
16/** 處于TIME_WAIT狀態(tài)的控制塊 */
17struct tcp_pcb *tcp_tw_pcbs;

tcp_bound_pcbs鏈表上的TCP控制塊可以看做是處于CLOSED狀態(tài),那些新綁定的端口初始的時(shí)候都是處于這個(gè)狀態(tài)。tcp_listen_pcbs鏈表用于記錄處于監(jiān)聽狀態(tài)的TCP控制塊,一般就是記錄tcp_pcb_listen控制塊。tcp_tw_pcbs鏈表用于記錄連接中處于TIME_WAIT狀態(tài)下的TCP控制塊。而tcp_active_pcbs鏈表用于記錄所有其他狀態(tài)(活動(dòng)狀態(tài))的TCP控制塊,這些端口是活躍的,可以不斷進(jìn)行狀態(tài)轉(zhuǎn)移。

窗口

關(guān)于窗口的理論我不想講太多,大家有興趣可以看一下網(wǎng)絡(luò)上的其他博文,都是描述得很詳細(xì)的。

TCP控制塊中關(guān)于接收窗口的成員變量有rcv_nxt、rcv_wnd、rcv_ann_wnd、rcv_ann_right_edge,rcv_nxt表示下次期望接收到的數(shù)據(jù)編號(hào),rcv_wnd表示接收窗口的大小,rcv_ann_wnd用于告訴發(fā)送方窗口的大小,rcv_ann_right_edge記錄了窗口的右邊界,這4個(gè)成員變量都會(huì)在數(shù)據(jù)傳輸?shù)倪^程中動(dòng)態(tài)改變的。

TCP控制塊中關(guān)于發(fā)送窗口的成員變量有lastack、snd_nxt、snd_lbb、snd_wnd,lastack記錄了已經(jīng)確認(rèn)的最大序號(hào),snd_nxt表示下次要發(fā)送的序號(hào),snd_lbb是表示下一個(gè)將被應(yīng)用線程緩沖的序號(hào),而snd_wnd表示發(fā)送窗口的大小,是由接收已方提供的。這些值也是動(dòng)態(tài)變化的,當(dāng)發(fā)送的數(shù)據(jù)收到確認(rèn),就會(huì)更新lastack,并且隨著數(shù)據(jù)的發(fā)送出去,窗口會(huì)向右移動(dòng),即snd_nxt的值在增加。

每條TCP 連接的每一端都必須設(shè)有兩個(gè)窗口:一個(gè)發(fā)送窗口和一個(gè)接收窗口,TCP 的可靠傳輸機(jī)制用字節(jié)的序號(hào)(編號(hào))進(jìn)行控制,TCP 所有的確認(rèn)都是基于數(shù)據(jù)的序號(hào)而不是基于報(bào)文段,發(fā)送過的數(shù)據(jù)未收到確認(rèn)之前必須保留,以便超時(shí)重傳時(shí)使用,發(fā)送窗口在沒收到確認(rèn)序號(hào)之前是保持不動(dòng)的,當(dāng)收到確認(rèn)序號(hào)就會(huì)向右移動(dòng),并且更新lastack 的值。

發(fā)送緩沖區(qū)用來暫時(shí)存放應(yīng)用程序發(fā)送給對(duì)方的數(shù)據(jù),這是主機(jī)已發(fā)送出但未收到確認(rèn)的數(shù)據(jù)。接收緩存用來暫時(shí)存放按序到達(dá)的、但尚未被接收應(yīng)用程序讀取的數(shù)據(jù)以及 不按序到達(dá)的數(shù)據(jù)。

關(guān)于窗口的概念必須強(qiáng)調(diào)2點(diǎn):

  1. 發(fā)送方的發(fā)送窗口并不總是和 接收方接收窗口一樣大,因?yàn)橛幸欢ǖ臅r(shí)間滯后。
  2. TCP 標(biāo)準(zhǔn)沒有規(guī)定對(duì)不按序到達(dá)的數(shù)據(jù)應(yīng)如何處理,通常是先臨時(shí)存放在接收窗口中,等到字節(jié)流中所缺少的字節(jié)收到后,再按序交付上層的應(yīng)用進(jìn)程。

TCP報(bào)文段發(fā)送

每個(gè)已經(jīng)連接的TCP控制塊中維護(hù)了3個(gè)是指針,分別是unsent、unacked、ooseq,unsent指向未發(fā)送的報(bào)文段緩沖隊(duì)列,unacked指向已發(fā)送但未收到確認(rèn)的報(bào)文段緩沖隊(duì)列,ooseq指向已經(jīng)收到的無序報(bào)文段緩沖隊(duì)列。

簡單來說TCP協(xié)議發(fā)送報(bào)文就是將應(yīng)用層的數(shù)據(jù)寫入發(fā)送緩沖區(qū)(緩沖隊(duì)列)中,根據(jù)窗口大小進(jìn)行發(fā)送。在LwIP中,為了更高效發(fā)送數(shù)據(jù),Nagle算法是默認(rèn)打開的。因此LwIP的處理是調(diào)用tcp_write()函數(shù)將應(yīng)用層的數(shù)據(jù)寫入TCP報(bào)文段緩沖隊(duì)列,即TCP控制塊中的unsent指針?biāo)赶虻年?duì)列中,但是不會(huì)立即發(fā)送,而是存儲(chǔ)在緩沖區(qū)里面,等待更多的數(shù)據(jù)進(jìn)行高效的發(fā)送。當(dāng)然只要你寫入的數(shù)據(jù)滿足Nagle算法的大小MSS,這是可以立即發(fā)送出去的,否則就等待超時(shí)或者數(shù)據(jù)達(dá)到MSS就會(huì)將數(shù)據(jù)發(fā)送出去。當(dāng)然,我們也能將Nagle算法禁用。ps:關(guān)于寫入緩沖隊(duì)列的操作大家可以直接看源碼即可。

當(dāng)然,我們也能手動(dòng)在寫入數(shù)據(jù)后直接調(diào)用TCP協(xié)議的發(fā)送數(shù)據(jù)函數(shù)來發(fā)送這些數(shù)據(jù)(RAW API 比較常用這種方法),LwIP是調(diào)用tcp_output()函數(shù)來發(fā)送這些數(shù)據(jù)的,這樣子一個(gè)應(yīng)用層的數(shù)據(jù)就通過TCP協(xié)議傳遞給IP層了。

關(guān)于Nagle算法的介紹,我引用維基百科的一段描述:

TCP/IP協(xié)議中,無論發(fā)送多少數(shù)據(jù),總是要在數(shù)據(jù)前面加上協(xié)議頭,同時(shí),對(duì)方接收到數(shù)據(jù),也需要發(fā)送ACK表示確認(rèn)。為了盡可能的利用網(wǎng)絡(luò)帶寬,TCP總是希望盡可能的發(fā)送足夠大的數(shù)據(jù)。(一個(gè)連接會(huì)設(shè)置MSS參數(shù),因此,TCP/IP希望每次都能夠以MSS尺寸的數(shù)據(jù)塊來發(fā)送數(shù)據(jù))。Nagle算法就是為了盡可能發(fā)送大塊數(shù)據(jù),避免網(wǎng)絡(luò)中充斥著許多小數(shù)據(jù)塊。

Nagle算法的基本定義是任意時(shí)刻,最多只能有一個(gè)未被確認(rèn)的小段。所謂“小段”,指的是小于MSS尺寸的數(shù)據(jù)塊,所謂“未被確認(rèn)”,是指一個(gè)數(shù)據(jù)塊發(fā)送出去后,沒有收到對(duì)方發(fā)送的ACK確認(rèn)該數(shù)據(jù)已收到。

代碼的實(shí)現(xiàn)如下:

1err_t
  2tcp_output(struct tcp_pcb *pcb)
  3{
  4  struct tcp_seg *seg, *useg;
  5  u32_t wnd, snd_nxt;
  6  err_t err;
  7  struct netif *netif;
  8
  9  //如果控制塊有數(shù)據(jù)在處理,直接返回
 10  if (tcp_input_pcb == pcb) {
 11    return ERR_OK;
 12  }
 13
 14  //得到合適的發(fā)送窗口
 15  wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
 16
 17  //找到控制塊中的未發(fā)送數(shù)據(jù)緩沖區(qū)鏈表
 18  seg = pcb->unsent;
 19
 20  //根據(jù)控制塊IP地址信息找到合適的網(wǎng)卡發(fā)送
 21  netif = tcp_route(pcb, &pcb->local_ip, &pcb->remote_ip);
 22  if (netif == NULL) {
 23    return ERR_RTE;
 24  }
 25
 26  /* 如果沒有本地IP地址,我們會(huì)從netif獲得一個(gè) */
 27  if (ip_addr_isany(&pcb->local_ip)) {
 28    const ip_addr_t *local_ip = 
 29    ip_netif_get_local_ip(netif, &pcb->remote_ip);
 30    if (local_ip == NULL) {
 31      return ERR_RTE;
 32    }
 33    ip_addr_copy(pcb->local_ip, *local_ip);
 34  }
 35
 36  /* 處理當(dāng)前不適合窗口的報(bào)文段 */
 37  if (lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd) 
 38  {
 39    //開始持續(xù)定時(shí)器
 40    if (wnd == pcb->snd_wnd && pcb->unacked == NULL &&
 41    pcb->persist_backoff == 0) 
 42    {
 43      pcb->persist_cnt = 0;
 44      pcb->persist_backoff = 1;
 45      pcb->persist_probe = 0;
 46    }
 47    /* 我們需要ACK,但現(xiàn)在無法發(fā)送數(shù)據(jù)(無法捎帶),所以發(fā)送一個(gè)ACK報(bào)文段 */
 48    if (pcb->flags & TF_ACK_NOW) {
 49      return tcp_send_empty_ack(pcb);
 50    }
 51    goto output_done;
 52  }
 53  /* 停止持續(xù)計(jì)時(shí)器 */
 54  pcb->persist_backoff = 0;
 55
 56  /* useg指向未應(yīng)答隊(duì)列中的最后一個(gè)tcp_seg結(jié)構(gòu) */
 57  useg = pcb->unacked;
 58  if (useg != NULL) {
 59    for (; useg->next != NULL; useg = useg->next);
 60  }
 61  /* 可用數(shù)據(jù)和窗口允許它發(fā)送報(bào)文段,直到把未發(fā)送鏈表的數(shù)據(jù)完全發(fā)送出去或者直到填滿發(fā)送窗口 */
 62  while (seg != NULL &&lwip_ntohl(seg->tcphdr->seqno)
 63         - pcb->lastack + seg->len <= wnd) 
 64    {
 65    if ((tcp_do_output_nagle(pcb) == 0) &&
 66        ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)) {
 67      break;
 68    }
 69
 70    if (pcb->state != SYN_SENT) {
 71      TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
 72    }
 73
 74    //真正發(fā)送TCP報(bào)文的函數(shù),此處發(fā)送TCP報(bào)文段
 75    err = tcp_output_segment(seg, pcb, netif);
 76
 77    if (err != ERR_OK) 
 78    {
 79      tcp_set_flags(pcb, TF_NAGLEMEMERR);
 80      return err;
 81    }
 82
 83    //得到下一個(gè)未發(fā)送的tcp_seg
 84    pcb->unsent = seg->next;
 85    if (pcb->state != SYN_SENT) 
 86    {
 87      tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
 88    }
 89    //計(jì)算snd_nxt的值
 90    snd_nxt = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
 91
 92    //更新下一個(gè)要發(fā)送的數(shù)據(jù)編號(hào)
 93    if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) 
 94    {
 95      pcb->snd_nxt = snd_nxt;
 96    }
 97    /* 如果發(fā)送出去的數(shù)據(jù)長度>0,則將這些報(bào)文段放在未確認(rèn)鏈表中 */
 98    if (TCP_TCPLEN(seg) > 0) 
 99    {
100      seg->next = NULL;
101      /* 未確認(rèn)鏈表為空,插入即可 */
102      if (pcb->unacked == NULL) {
103        pcb->unacked = seg;
104        useg = seg;
105
106      } 
107      //如果不為空,按照順序插入未確認(rèn)鏈表中
108      else 
109      {
110        if (TCP_SEQ_LT(lwip_ntohl(seg->tcphdr->seqno),
111        lwip_ntohl(useg->tcphdr->seqno))) 
112        {
113          struct tcp_seg **cur_seg = &(pcb->unacked);
114          while (*cur_seg &&
115                 TCP_SEQ_LT(lwip_ntohl((*cur_seg)->tcphdr->seqno), lwip_ntohl(seg->tcphdr->seqno))) {
116            cur_seg = &((*cur_seg)->next );
117          }
118          seg->next = (*cur_seg);
119          (*cur_seg) = seg;
120        }
121        else 
122        {
123          useg->next = seg;
124          useg = useg->next;
125        }
126      }
127    }
128    else 
129    {
130      tcp_seg_free(seg);
131    }
132    seg = pcb->unsent;
133  }
134
135output_done:
136  tcp_clear_flags(pcb, TF_NAGLEMEMERR);
137  return ERR_OK;
138}

總的來說,流程還是很簡單明了的,如果控制塊的flags字段被設(shè)置為TF_ACK_NOW,表示需要立即響應(yīng)對(duì)方,但是此時(shí)還沒有數(shù)據(jù)發(fā)送,就只發(fā)送一個(gè)純粹的ACK應(yīng)答報(bào)文段,如果能發(fā)送數(shù)據(jù),那就將ACK應(yīng)答捎帶過去(捎帶機(jī)制),這樣子就能減少網(wǎng)絡(luò)中的流量。TCP協(xié)議在發(fā)送的時(shí)候先找到未發(fā)送隊(duì)列unsent,然后調(diào)用tcp_output_segment()->ip_output_if()函數(shù)進(jìn)行發(fā)送,將TCP報(bào)文段傳遞到IP層,直到把未發(fā)送隊(duì)列的數(shù)據(jù)完全發(fā)送出去或者直到填滿發(fā)送窗口才算是完成一次發(fā)送操作,同時(shí)要更新發(fā)送窗口相關(guān)字段,還要將這些已發(fā)送但是未確認(rèn)的數(shù)據(jù)存儲(chǔ)在已發(fā)送但未確認(rèn)鏈表unacked中,以防丟失數(shù)據(jù)進(jìn)行重發(fā)操作,放入未確認(rèn)鏈表的時(shí)候是按序號(hào)升序進(jìn)行排序的。

TCP報(bào)文段接收

IP數(shù)據(jù)報(bào)中如果是遞交給TCP協(xié)議的數(shù)據(jù),就會(huì)調(diào)用tcp_input()函數(shù)往上層傳遞,因此TCP協(xié)議的報(bào)文段接收函數(shù)就是tcp_input()函數(shù)。只不過這個(gè)函數(shù)太過于復(fù)雜,我自己都不想看它,就簡單用文字描述一下處理過程吧,然后刪減一下代碼讓大伙看看。

tcp_input()函數(shù)會(huì)對(duì)傳遞進(jìn)來的IP數(shù)據(jù)報(bào)進(jìn)行處理,做一些校驗(yàn),檢查數(shù)據(jù)報(bào)是否正確等操作,查看一下數(shù)據(jù)報(bào)中是否有數(shù)據(jù),如果沒有就丟掉,再看一下是不是多播、廣播報(bào)文,如果是就不做處理,釋放pbuf。將TCP首部中的各字段內(nèi)容提取出來,首先在 tcp_active_pcbs鏈表中尋找對(duì)應(yīng)的TCP控制塊,找到了就調(diào)用tcp_process()函數(shù)進(jìn)行處理;如果找不到就去tcp_tw_pcbs鏈表中查找,找到了就調(diào)用tcp_timewait_input()函數(shù)處理它;如果還是找不到就去tcp_listen_pcbs鏈表中找,如果找到就調(diào)用tcp_listen_input()函數(shù)處理,如果還是找不到的話,那沒辦法了,這收到的是垃圾數(shù)據(jù),釋放pbu。

還要注意的是,TCP協(xié)議很可能收到不是正常數(shù)據(jù),而是一些特殊TCP報(bào)文段

  1. 如果收到的是復(fù)位報(bào)文終止連接應(yīng)答報(bào)文,那么就釋放pbuf,終止連接
  2. 如果是收到了應(yīng)答報(bào)文段(發(fā)送數(shù)據(jù)后必須等待應(yīng)答),那么就調(diào)用宏TCP_EVENT_SENT(其實(shí)是一個(gè)sent的回調(diào)函數(shù))去處理,并且更新窗口
  3. 如果報(bào)文段中包含有效的數(shù)據(jù),就調(diào)用TCP_EVENT_RECV去處理它,此時(shí)將產(chǎn)生應(yīng)答報(bào)文與更新接收窗口的操作
  4. 如果是收到FIN報(bào)文,則調(diào)用TCP_EVENT_CLOSED去處理它,此時(shí)將產(chǎn)生應(yīng)答并且開始終止連接

代碼如下:

1void
  2tcp_input(struct pbuf *p, struct netif *inp)
  3{
  4  struct tcp_pcb *pcb, *prev;
  5  struct tcp_pcb_listen *lpcb;
  6
  7  u8_t hdrlen_bytes;
  8  err_t err;
  9
 10  LWIP_UNUSED_ARG(inp);
 11
 12  PERF_START;
 13
 14  TCP_STATS_INC(tcp.recv);
 15  MIB2_STATS_INC(mib2.tcpinsegs);
 16
 17  tcphdr = (struct tcp_hdr *)p->payload;
 18
 19  /* 檢查報(bào)文段是否有有效數(shù)據(jù) */
 20  if (p->len < TCP_HLEN) 
 21  {
 22    /* 如果沒有就丟掉報(bào)文段 */
 23    TCP_STATS_INC(tcp.lenerr);
 24    goto dropped;
 25  }
 26
 27  /* 不處理傳入的廣播/多播報(bào)文段。 */
 28  if (ip_addr_isbroadcast(ip_current_dest_addr(), 
 29      ip_current_netif()) ||
 30      ip_addr_ismulticast(ip_current_dest_addr())) 
 31  {
 32    TCP_STATS_INC(tcp.proterr);
 33    goto dropped;
 34  }
 35
 36  /* 檢查TCP報(bào)文段首部長度 */
 37  hdrlen_bytes = TCPH_HDRLEN_BYTES(tcphdr);
 38  if ((hdrlen_bytes < TCP_HLEN) || (hdrlen_bytes > p->tot_len)) 
 39  {
 40    TCP_STATS_INC(tcp.lenerr);
 41    goto dropped;
 42  }
 43
 44  /* 移動(dòng)pbuf指針,指向TCP報(bào)文段數(shù)據(jù)區(qū)域 */
 45  tcphdr_optlen = (u16_t)(hdrlen_bytes - TCP_HLEN);
 46  tcphdr_opt2 = NULL;
 47  if (p->len >= hdrlen_bytes) 
 48  {
 49    tcphdr_opt1len = tcphdr_optlen;
 50    pbuf_remove_header(p, hdrlen_bytes); 
 51  } 
 52
 53  /* 將TCP首部中的各字段內(nèi)容提取出來。 */
 54  tcphdr->src = lwip_ntohs(tcphdr->src);
 55  tcphdr->dest = lwip_ntohs(tcphdr->dest);
 56  seqno = tcphdr->seqno = lwip_ntohl(tcphdr->seqno);
 57  ackno = tcphdr->ackno = lwip_ntohl(tcphdr->ackno);
 58  tcphdr->wnd = lwip_ntohs(tcphdr->wnd);
 59
 60  flags = TCPH_FLAGS(tcphdr);
 61  tcplen = p->tot_len;
 62
 63  if (flags & (TCP_FIN | TCP_SYN)) 
 64  {
 65    tcplen++;
 66    if (tcplen < p->tot_len) 
 67    {
 68      /* u16_t溢出,無法處理這個(gè) */
 69      TCP_STATS_INC(tcp.lenerr);
 70      goto dropped;
 71    }
 72  }
 73
 74  prev = NULL;
 75
 76  //遍歷tcp_active_pcbs鏈表尋找對(duì)應(yīng)的TCP控制塊
 77  for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) 
 78  {
 79    /* 檢查控制塊是否與對(duì)應(yīng)的網(wǎng)卡綁定 */
 80    if ((pcb->netif_idx != NETIF_NO_INDEX) &&
 81        (pcb->netif_idx != 
 82        netif_get_index(ip_data.current_input_netif))) 
 83    {
 84      prev = pcb;
 85      continue;
 86    }
 87     /* ··· */
 88     /* 省略處理 */
 89     /* ··· */
 90
 91  if (pcb == NULL) 
 92  {
 93    /* 如果TCP控制塊沒有處于連接狀態(tài),就去tcp_tw_pcbs鏈表中找 */
 94    for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) 
 95    {
 96      /* 檢查控制塊是否與對(duì)應(yīng)的網(wǎng)卡綁定 */
 97      if ((pcb->netif_idx != NETIF_NO_INDEX) &&
 98          (pcb->netif_idx != netif_get_index
 99          (ip_data.current_input_netif))) 
100          {
101            continue;
102          }
103
104      if (pcb->remote_port == tcphdr->src &&
105          pcb->local_port == tcphdr->dest &&
106          ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()) &&
107          ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) 
108      {
109            //找到了就處理它
110            tcp_timewait_input(pcb);
111
112        pbuf_free(p);
113        return;
114      }
115    }
116
117    /* 還是找不到就去tcp_listen_pcbs鏈表中找 */
118    prev = NULL;
119    for (lpcb = tcp_listen_pcbs.listen_pcbs; 
120    lpcb != NULL; lpcb = lpcb->next) 
121    {
122      /* 檢查控制塊是否與對(duì)應(yīng)的網(wǎng)卡綁定 */
123      if ((lpcb->netif_idx != NETIF_NO_INDEX) &&
124          (lpcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
125        prev = (struct tcp_pcb *)lpcb;
126        continue;
127      }
128     /* ··· */
129     /* 省略處理 */
130     /* ··· */
131
132    //找到了處于監(jiān)聽狀態(tài)的TCP控制塊
133    if (lpcb != NULL) 
134    {
135      if (prev != NULL) {
136        ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
137        lpcb->next = tcp_listen_pcbs.listen_pcbs;
138        tcp_listen_pcbs.listen_pcbs = lpcb;
139      } else {
140        TCP_STATS_INC(tcp.cachehit);
141      }
142      //處理報(bào)文段
143      tcp_listen_input(lpcb);
144      pbuf_free(p);
145      return;
146    }
147  }
148
149     /* ··· */
150     /* 省略處理 */
151     /* ··· */
152
153    tcp_input_pcb = pcb;
154    err = tcp_process(pcb);
155
156     /* ··· */
157     /* 省略處理 */
158     /* ··· */
159
160    }
161}
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4697

    瀏覽量

    68085
  • 變量
    +關(guān)注

    關(guān)注

    0

    文章

    609

    瀏覽量

    28288
  • LwIP
    +關(guān)注

    關(guān)注

    2

    文章

    85

    瀏覽量

    27024
  • 結(jié)構(gòu)體
    +關(guān)注

    關(guān)注

    1

    文章

    127

    瀏覽量

    10812
  • TCP協(xié)議
    +關(guān)注

    關(guān)注

    1

    文章

    91

    瀏覽量

    12050
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    STM32之LWIP網(wǎng)絡(luò)協(xié)議TCP服務(wù)器創(chuàng)建

    lwip是瑞典計(jì)算機(jī)科學(xué)院網(wǎng)絡(luò)嵌入式系統(tǒng)小組(SICS)的Adam Dunkels(亞當(dāng)·鄧克爾) 開發(fā)的一個(gè)小型開源的TCP/IP協(xié)議棧。實(shí)現(xiàn)的重點(diǎn)是在保持
    的頭像 發(fā)表于 06-02 08:44 ?9503次閱讀
    STM32之<b class='flag-5'>LWIP</b>網(wǎng)絡(luò)<b class='flag-5'>協(xié)議</b>棧<b class='flag-5'>TCP</b>服務(wù)器創(chuàng)建

    基于MM32F5270的Ethernet實(shí)現(xiàn)LwIP協(xié)議棧移植

    LwIP是輕量化的TCP/IP協(xié)議,由瑞典計(jì)算機(jī)科學(xué)院(SICS)的Adam Dunkels 開發(fā)的一個(gè)小型開源的TCP/IP協(xié)議棧。
    的頭像 發(fā)表于 06-21 10:28 ?1017次閱讀
    基于MM32F5270的Ethernet<b class='flag-5'>實(shí)現(xiàn)</b><b class='flag-5'>LwIP</b><b class='flag-5'>協(xié)議</b>棧移植

    lwip tcp ip 協(xié)議

    本帖最后由 mr.pengyongche 于 2013-4-30 03:11 編輯 lwip tcp ip 協(xié)議
    發(fā)表于 02-22 16:49

    LWIP TCP報(bào)文基礎(chǔ)知識(shí)及其LWIPTCP協(xié)議實(shí)現(xiàn)

    ,只有URG標(biāo)志位被設(shè)值時(shí)該字段才有意義,表示緊急數(shù)據(jù)相對(duì)序列號(hào)(Sequence Number字段的值)的偏移。用URG值+序號(hào)得到最后一個(gè)緊急字節(jié)。LWIPTCP協(xié)議
    發(fā)表于 10-18 14:54

    Lwip協(xié)議棧的設(shè)計(jì)方案

    LWIPTCP/IP協(xié)議棧的一種實(shí)現(xiàn)。LWIP的主要目的是減少存儲(chǔ)器利用量和代碼尺寸,使LWIP
    發(fā)表于 09-16 15:18 ?33次下載
    <b class='flag-5'>Lwip</b><b class='flag-5'>協(xié)議</b>棧的設(shè)計(jì)方案

    lwip協(xié)議中文版

    LWIPTCP/IP協(xié)議棧的一種實(shí)現(xiàn)。LWIP的主要目的是減少存儲(chǔ)器利用量和代碼尺寸,使LWIP
    發(fā)表于 02-03 16:47 ?0次下載
    <b class='flag-5'>lwip</b><b class='flag-5'>協(xié)議</b>中文版

    LwIP協(xié)議詳解

    LwIP協(xié)議詳解,LwIP是Light Weight (輕型)IP協(xié)議,有無操作系統(tǒng)的支持都可以運(yùn)行。LwIP
    發(fā)表于 11-09 18:25 ?49次下載

    TCPIP協(xié)議棧的實(shí)現(xiàn)lwip

    TCPIP協(xié)議棧的實(shí)現(xiàn)lwip方便初學(xué)者剛開始接觸lwip,有個(gè)大概的了解與認(rèn)識(shí)。
    發(fā)表于 03-14 15:40 ?13次下載

    lwip協(xié)議棧源碼詳解說明

    lwip是瑞典計(jì)算機(jī)科學(xué)院(SICS)的Adam Dunkels 開發(fā)的一個(gè)小型開源的TCP/IP協(xié)議棧。實(shí)現(xiàn)的重點(diǎn)是在保持TCP
    發(fā)表于 12-11 15:27 ?3.7w次閱讀
    <b class='flag-5'>lwip</b><b class='flag-5'>協(xié)議</b>棧源碼詳解說明

    介紹tcp_ip協(xié)議lwip的特點(diǎn)

    簡介了嵌入式tcp_ip協(xié)議lwip的基本信息
    的頭像 發(fā)表于 07-03 13:05 ?3594次閱讀

    LWIP協(xié)議Raw TCP中使用

    本文檔的主要內(nèi)容詳細(xì)介紹的是LWIP協(xié)議Raw TCP中使用的資料免費(fèi)下載
    發(fā)表于 11-05 17:36 ?17次下載
    <b class='flag-5'>LWIP</b><b class='flag-5'>協(xié)議</b>棧<b class='flag-5'>中</b>Raw <b class='flag-5'>TCP</b>中使用

    基于μcosⅡ和S3C2410實(shí)現(xiàn)TCP/IP協(xié)議LwIP的應(yīng)用方案

    隨著嵌入式系統(tǒng)與網(wǎng)絡(luò)的日益結(jié)合,越來越多的嵌入式設(shè)備需要實(shí)現(xiàn)Internet網(wǎng)絡(luò)化,支持嵌入式設(shè)備接入網(wǎng)絡(luò),已成為嵌入式領(lǐng)域重要的研究方向。而目前嵌入式系統(tǒng)中大量應(yīng)用低速處理器,受內(nèi)存和速度限制,實(shí)現(xiàn)完整的TCP/IP
    的頭像 發(fā)表于 06-22 15:36 ?1797次閱讀
    基于μcosⅡ和S3C2410<b class='flag-5'>實(shí)現(xiàn)</b><b class='flag-5'>TCP</b>/IP<b class='flag-5'>協(xié)議</b>棧<b class='flag-5'>LwIP</b>的應(yīng)用方案

    如何更好的理解LWIP協(xié)議

    LwIP(Light weight IP),是一種輕量化且開源的TCP/IP協(xié)議棧,它可以在有限的RAM和ROM條件下,實(shí)現(xiàn)一個(gè)完整的TCP
    的頭像 發(fā)表于 10-27 09:13 ?4086次閱讀

    基于LwIPTCP客戶端設(shè)計(jì)

    上一篇我們基于LwIP協(xié)議棧的RAW API實(shí)現(xiàn)了一個(gè)TCP服務(wù)器的簡單應(yīng)用,接下來一節(jié)我們來實(shí)現(xiàn)一個(gè)T
    的頭像 發(fā)表于 12-14 15:12 ?2201次閱讀
    基于<b class='flag-5'>LwIP</b>的<b class='flag-5'>TCP</b>客戶端設(shè)計(jì)

    LwIP協(xié)議棧源碼詳解—TCP/IP協(xié)議實(shí)現(xiàn)

    電子發(fā)燒友網(wǎng)站提供《LwIP協(xié)議棧源碼詳解—TCP/IP協(xié)議實(shí)現(xiàn).pdf》資料免費(fèi)下載
    發(fā)表于 07-03 11:22 ?3次下載