前言說(shuō)明
本文章代碼非常多,并且難懂,如非特別需要,否則不建議閱讀!建議學(xué)習(xí)TCP協(xié)議理論,等基礎(chǔ)扎實(shí)后再去閱讀lwip源碼,本文章的源碼只是輔助真正有需要的人閱讀!
TCP控制塊
與其他協(xié)議一樣,為了描述TCP
協(xié)議,LwIP定義了一個(gè)名字叫tcp_pcb
的結(jié)構(gòu)體,可以稱(chēng)之為TCP控制塊
,其內(nèi)定義了大量的成員變量,基本定義了整個(gè)TCP協(xié)議運(yùn)作過(guò)程的所有需要的東西,如發(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; /* 用于測(cè)試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ù)類(lèi)型 */ 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īng)狀態(tài)
的TCP連接,因?yàn)榉峙渫暾?code>TCP控制塊是比較消耗內(nèi)存資源的,而TCP協(xié)議在建立連接之前是無(wú)傳輸數(shù)據(jù)的,因此在監(jiān)聽(tīng)的時(shí)候只需要把建立連接的主機(jī)的相關(guān)信息得到,然后無(wú)縫切換到完整的TCP控制塊中,這樣子就能節(jié)省不少資源(畢竟在嵌入式設(shè)備中資源是有限的)。除了tcp_pcb_listen
外,LwIP還定義了4
個(gè)鏈表來(lái)維護(hù)TCP
連接時(shí)的各種狀態(tài),分別是:
新綁定的端口鏈表
,用于記錄新綁定端口
的TCP控制塊。監(jiān)聽(tīng)鏈表
:用于記錄處于監(jiān)聽(tīng)狀態(tài)
的TCP控制塊 。活動(dòng)狀態(tài)鏈表
:用于記錄處于其他(活動(dòng))狀態(tài)
的TCP控制塊。TIME_WAIT鏈表
:用于記錄處于TIME_WAIT狀態(tài)
的控制塊。
1/** 用于監(jiān)聽(tīng)的TCP協(xié)議控制塊 */
2struct tcp_pcb_listen {
3/** 所有PCB類(lèi)型的通用成員 */
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īng)狀態(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īng)狀態(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ù)倪^(guò)程中動(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ā)送過(guò)的數(shù)據(jù)未收到確認(rèn)之前必須保留,以便超時(shí)重傳時(shí)使用,發(fā)送窗口在沒(méi)收到確認(rèn)序號(hào)之前是保持不動(dòng)的,當(dāng)收到確認(rèn)序號(hào)就會(huì)向右移動(dòng),并且更新lastack
的值。
發(fā)送緩沖區(qū)用來(lái)暫時(shí)存放應(yīng)用程序發(fā)送給對(duì)方的數(shù)據(jù),這是主機(jī)已發(fā)送出但未收到確認(rèn)的數(shù)據(jù)。接收緩存用來(lái)暫時(shí)存放按序到達(dá)的、但尚未被接收應(yīng)用程序讀取的數(shù)據(jù)以及 不按序到達(dá)的數(shù)據(jù)。
關(guān)于窗口的概念必須強(qiáng)調(diào)2點(diǎn):
- 發(fā)送方的發(fā)送窗口并不總是和 接收方接收窗口一樣大,因?yàn)橛幸欢ǖ臅r(shí)間滯后。
- TCP 標(biāo)準(zhǔn)沒(méi)有規(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)收到的無(wú)序報(bào)文段緩沖隊(duì)列。
簡(jiǎn)單來(lái)說(shuō)TCP協(xié)議發(fā)送報(bào)文就是將應(yīng)用層的數(shù)據(jù)寫(xiě)入發(fā)送緩沖區(qū)(緩沖隊(duì)列)中,根據(jù)窗口大小進(jìn)行發(fā)送。在LwIP
中,為了更高效發(fā)送數(shù)據(jù),Nagle算法是默認(rèn)打開(kāi)的。因此LwIP的處理是調(diào)用tcp_write()
函數(shù)將應(yīng)用層的數(shù)據(jù)寫(xiě)入TCP
報(bào)文段緩沖隊(duì)列,即TCP控制塊中的unsent
指針?biāo)赶虻年?duì)列中,但是不會(huì)立即
發(fā)送,而是存儲(chǔ)在緩沖區(qū)里面,等待更多的數(shù)據(jù)進(jìn)行高效的發(fā)送。當(dāng)然只要你寫(xiě)入的數(shù)據(jù)滿(mǎn)足Nagle算法
的大小MSS
,這是可以立即發(fā)送出去的,否則就等待超時(shí)或者數(shù)據(jù)達(dá)到MSS
就會(huì)將數(shù)據(jù)發(fā)送出去。當(dāng)然,我們也能將Nagle算法
禁用。ps:關(guān)于寫(xiě)入緩沖隊(duì)列的操作大家可以直接看源碼即可。
當(dāng)然,我們也能手動(dòng)在寫(xiě)入數(shù)據(jù)后直接調(diào)用TCP協(xié)議的發(fā)送數(shù)據(jù)函數(shù)來(lái)發(fā)送這些數(shù)據(jù)(RAW API
比較常用這種方法),LwIP
是調(diào)用tcp_output()
函數(shù)來(lái)發(fā)送這些數(shù)據(jù)的,這樣子一個(gè)應(yīng)用層的數(shù)據(jù)就通過(guò)TCP協(xié)議傳遞給IP層了。
關(guān)于Nagle算法
的介紹,我引用維基百科的一段描述:
TCP/IP協(xié)議中,無(wú)論發(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ù)塊來(lái)發(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ā)送出去后,沒(méi)有收到對(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 /* 如果沒(méi)有本地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 //開(kāi)始持續(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)在無(wú)法發(fā)送數(shù)據(jù)(無(wú)法捎帶),所以發(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ā)送出去或者直到填滿(mǎn)發(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ù)長(zhǎng)度>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}
總的來(lái)說(shuō),流程還是很簡(jiǎn)單明了的,如果控制塊的flags
字段被設(shè)置為TF_ACK_NOW
,表示需要立即響應(yīng)對(duì)方,但是此時(shí)還沒(méi)有數(shù)據(jù)發(fā)送,就只發(fā)送一個(gè)純粹的ACK
應(yīng)答報(bào)文段,如果能發(fā)送數(shù)據(jù),那就將ACK
應(yīng)答捎帶過(guò)去(捎帶機(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ā)送
出去或者直到填滿(mǎn)發(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ù)。只不過(guò)這個(gè)函數(shù)太過(guò)于復(fù)雜,我自己都不想看它,就簡(jiǎn)單用文字描述一下處理過(guò)程吧,然后刪減一下代碼讓大伙看看。
tcp_input()
函數(shù)會(huì)對(duì)傳遞進(jìn)來(lái)的IP數(shù)據(jù)報(bào)
進(jìn)行處理,做一些校驗(yàn)
,檢查數(shù)據(jù)報(bào)是否正確等操作,查看一下數(shù)據(jù)報(bào)中是否有數(shù)據(jù)
,如果沒(méi)有就丟掉,再看一下是不是多播、廣播
報(bào)文,如果是就不做處理,釋放pbuf。將TCP
首部中的各字段內(nèi)容提取出來(lá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ù)處理,如果還是找不到的話(huà),那沒(méi)辦法了,這收到的是垃圾數(shù)據(jù),釋放pbu。
還要注意的是,TCP協(xié)議很可能收到不是正常數(shù)據(jù),而是一些特殊TCP報(bào)文段
:
- 如果收到的是
復(fù)位報(bào)文
或終止連接應(yīng)答報(bào)文
,那么就釋放pbuf,終止連接 - 如果是收到了
應(yīng)答報(bào)文段
(發(fā)送數(shù)據(jù)后必須等待應(yīng)答),那么就調(diào)用宏TCP_EVENT_SENT
(其實(shí)是一個(gè)sent的回調(diào)函數(shù))去處理,并且更新窗口 - 如果報(bào)文段中包含有效的數(shù)據(jù),就調(diào)用
TCP_EVENT_RECV
去處理它,此時(shí)將產(chǎn)生應(yīng)答報(bào)文與更新接收窗口的操作 - 如果是收到FIN報(bào)文,則調(diào)用
TCP_EVENT_CLOSED
去處理它,此時(shí)將產(chǎn)生應(yīng)答并且開(kāi)始終止連接
代碼如下:
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 /* 如果沒(méi)有就丟掉報(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 /* 檢查T(mén)CP報(bào)文段首部長(zhǎng)度 */
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)容提取出來(lá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溢出,無(wú)法處理這個(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控制塊沒(méi)有處于連接狀態(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īng)狀態(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}
-
代碼
+關(guān)注
關(guān)注
30文章
4788瀏覽量
68617 -
變量
+關(guān)注
關(guān)注
0文章
613瀏覽量
28371 -
LwIP
+關(guān)注
關(guān)注
2文章
86瀏覽量
27173 -
結(jié)構(gòu)體
+關(guān)注
關(guān)注
1文章
130瀏覽量
10844 -
TCP協(xié)議
+關(guān)注
關(guān)注
1文章
91瀏覽量
12070
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論