TCP本身是一個(gè)相對(duì)復(fù)雜的協(xié)議,Lwip中最復(fù)雜的部分也是此處。這里,我們分部分描述。
第一部分:TCP處理
Tcp.c該文件提供了一些通用的函數(shù)接口。該文件中的函數(shù)主要的操作對(duì)象就是tcp_pcb,包括對(duì)tcp pcb的設(shè)置,修改讀取等。另外,在該文件中還實(shí)現(xiàn)了tcp的定時(shí)器。
目錄
一:Tcp的一些超時(shí)變量
二:Tcp的控制塊(pcb)數(shù)據(jù)結(jié)構(gòu)
1 計(jì)時(shí)變量
2 輪回時(shí)間評(píng)估變量
3 快速重傳和快速恢復(fù)變量
4 擁塞避免與控制變量
5 發(fā)送序號(hào)、窗口相關(guān)變量
6 接收序號(hào)、窗口相關(guān)變量
7 Tcp段
三:定時(shí)器
3.1 Tcp的快速定時(shí)器:
3.2 Tcp的慢速定時(shí)器:
3.2.1 active狀態(tài)pcb的超時(shí)處理
3.2.2 time-wait狀態(tài)pcb的超時(shí)處理
一:Tcp的一些超時(shí)變量
#define TCP_TMR_INTERVAL?????? 250?
#define TCP_FAST_INTERVAL???? ?TCP_TMR_INTERVAL
#define TCP_SLOW_INTERVAL????? (2*TCP_TMR_INTERVAL)?
TCP處理內(nèi)部時(shí)間基準(zhǔn)為250毫秒
快速定時(shí)器為250毫秒
慢速定時(shí)器為500毫秒
#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */
#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */
#define TCP_OOSEQ_TIMEOUT?????? ?6 /* x RTO */
#define TCP_MSL 60000? /* The maximum segment lifetime in microseconds */
MSL時(shí)間為60秒
FIN狀態(tài)超時(shí)時(shí)間20秒
SYN_RECVD等待狀態(tài)超時(shí)時(shí)間20秒
序號(hào)外數(shù)據(jù)超時(shí)時(shí)間6RTO
#define??? TCP_NODELAY??? ?? 0x01? ?? /* don't delay send to coalesce packets */
如果支持no delay,那么所有發(fā)送的數(shù)據(jù)包將不被緩存。TCP協(xié)議對(duì)一些零散的小包采取合并發(fā)送,這樣可以減少交互過(guò)程,提高帶寬利用率,副作用是一些包的發(fā)送將被延遲。
#define TCP_KEEPALIVE? 0x02??? /* send KEEPALIVE probes when idle for pcb->keepalive miliseconds */
/* Keepalive values */
#define? TCP_KEEPDEFAULT?? 7200000???? /* KEEPALIVE timer in miliseconds */
#define? TCP_KEEPINTVL? 75000? /* Time between KEEPALIVE probes in miliseconds */
#define ?TCP_KEEPCNT?????? 9?? /* Counter for KEEPALIVE probes */
#define? TCP_MAXIDLE ?TCP_KEEPCNT * TCP_KEEPINTVL? /* Maximum KEEPALIVE probe time */
如果定義了keepalive選項(xiàng),則要用到上面這些變量
Keepalive探測(cè)發(fā)送前等待的時(shí)間? 7200秒 120分鐘 2小時(shí)
發(fā)送keepalive探測(cè)的時(shí)間間隔 75秒 1分鐘多
Keepalive探測(cè)發(fā)送的次數(shù) 9次
Keepalive總的可消耗時(shí)間 9*75秒 675秒 10分鐘多
二:Tcp的控制塊(pcb)數(shù)據(jù)結(jié)構(gòu)
1 計(jì)時(shí)變量
tmr內(nèi)部的計(jì)時(shí)變量,該變量包存了對(duì)應(yīng)連接最后一次處于活躍狀態(tài)的時(shí)間
polltmr poll定時(shí)計(jì)數(shù)
pollinterval Poll定時(shí)器間隔
rtime 重傳計(jì)數(shù)
2 輪回時(shí)間評(píng)估變量
rttest、rtseq、sa以及sv等域被用來(lái)進(jìn)行輪回時(shí)間評(píng)估。被用來(lái)進(jìn)行輪回時(shí)間評(píng)估的段的序號(hào)被保存在rtseq中,該段被發(fā)送的時(shí)間保存在rttest中。平均輪回時(shí)間和輪回時(shí)間變量被保存在變量sa和sv中。在計(jì)算重傳超時(shí)時(shí)間時(shí)這些變量就被使用,而這個(gè)基準(zhǔn)的重傳超時(shí)量保存在rto域中。Nrtx保存有重傳的次數(shù)
3 快速重傳和快速恢復(fù)變量
lastack和dupacks兩個(gè)域在實(shí)現(xiàn)快速重傳與快速恢復(fù)的實(shí)現(xiàn)中使用。lastack域保存被接收到的最后一個(gè)ACK確認(rèn)的序列號(hào),dupacks指示有多少個(gè)這樣的ACKs,這些已經(jīng)被接收到的ACK序列號(hào)保存在lastack中。
4 擁塞避免與控制變量
當(dāng)前連接的擁賽控制窗口被保存在cwnd域中,而慢啟動(dòng)的閾值保存在ssthresh中。
5 發(fā)送序號(hào)、窗口相關(guān)變量
Snd_nxt 保存有下一個(gè)要發(fā)送的序號(hào)
Snd_max 保存有最大發(fā)送序號(hào)
Snd_wnd 發(fā)送者窗口
Snd_wl1 保存有窗口最近更新段的序號(hào)
Snd_wl2 保存有窗口最近更新段的確認(rèn)號(hào)
Snd_lbb 下一個(gè)將要被緩沖的數(shù)據(jù)字節(jié)序號(hào)
Acked 最新被確認(rèn)的數(shù)據(jù)字節(jié)序號(hào)
Snd_buf 以字節(jié)為單位的可用發(fā)送緩沖空間
Snd_queuelen 以段為單位的可用發(fā)送緩沖空間
以上六個(gè)域ackd、snd_nxt、snd_wnd、snd_wl1、snd_wl2和snd_lbb在發(fā)送數(shù)據(jù)時(shí)使用。被接收者確認(rèn)的最高的序列號(hào)保存在ackd中,已發(fā)送的數(shù)據(jù)的最大序號(hào)保存在snd_max中,下一個(gè)將要發(fā)送的序列號(hào)由snd_nxt保存。接收者的建議窗口由snd_wnd保存,snd_wl1和snd_wl2在更新snd_wnd時(shí)使用。snd_lbb含有傳輸隊(duì)列中最后一個(gè)字節(jié)的序號(hào)。
6 接收序號(hào)、窗口相關(guān)變量
rcv_nxt與rcv_wnd變量在接收數(shù)據(jù)時(shí)使用。rcv_nxt域含有下一個(gè)期望從遠(yuǎn)程終端得到的字節(jié)序號(hào),因此在發(fā)送ACKs到遠(yuǎn)程主機(jī)時(shí)使用。接收者的窗口被rcv_wnd保持,在發(fā)送TCP段中會(huì)突出這一點(diǎn)。tmr域被用來(lái)作為一個(gè)定時(shí)器,在特定長(zhǎng)度時(shí)間過(guò)去之后,當(dāng)前TCP連接就應(yīng)當(dāng)被移除,比如像處在TIME-WAIT狀態(tài)的連接。鏈路上所允許的最大段的大小被保存在mss域中,flags域保存有鏈路的額外的狀態(tài)信息,像連接是否屬于快速恢復(fù),或者一個(gè)延遲的ACK是否應(yīng)當(dāng)被發(fā)送。
7 Tcp段
unsent、unacked和ooseq三個(gè)隊(duì)列在發(fā)送和接收數(shù)據(jù)時(shí)使用。從應(yīng)用層接收到但是還沒(méi)有發(fā)送的數(shù)據(jù)被加入unsent隊(duì)列,已經(jīng)發(fā)送但是還沒(méi)有被遠(yuǎn)程主機(jī)確認(rèn)的數(shù)據(jù)保存在unacked隊(duì)列,接收到的序外數(shù)據(jù)緩沖在ooseq中。
后續(xù)考慮增加與窗口擴(kuò)大選項(xiàng)有關(guān)的變量。根據(jù)定義,當(dāng)擴(kuò)大因子不為零時(shí),即使為1,也需要大約128k的緩存空間。65535*2(1)
三:定時(shí)器
協(xié)議中需要的定時(shí)器? 總共有7個(gè)
連接建立定時(shí)器:syndrome發(fā)送后一定時(shí)間內(nèi)沒(méi)有收到響應(yīng),終止連接的建立
重傳定時(shí)器:當(dāng)長(zhǎng)時(shí)間沒(méi)有收到對(duì)端的acknowledge時(shí)會(huì)觸發(fā)該定時(shí)器,認(rèn)為數(shù)據(jù)包可能丟失,而進(jìn)行重傳。該定時(shí)器的時(shí)間是動(dòng)態(tài)計(jì)算的
延遲acknowledge定時(shí)器:
堅(jiān)持定時(shí)器:有發(fā)送方發(fā)出稱為窗口探查的報(bào)文段,以避免在零窗口時(shí)產(chǎn)生死鎖。
?;疃〞r(shí)器:?;疃〞r(shí)器用于在連接的雙方或者其中的一方出現(xiàn)故障時(shí),通過(guò)發(fā)送?;顖?bào)文使客戶或者服務(wù)器能夠發(fā)現(xiàn)這種問(wèn)題。此時(shí)連接可能長(zhǎng)時(shí)間空閑,這時(shí)觸發(fā)?;疃〞r(shí)器的一個(gè)條件。
Fin-wait-2定時(shí)器:避免進(jìn)入該狀態(tài)后長(zhǎng)時(shí)間收不到對(duì)端的fin。超時(shí)后關(guān)閉連接
Time-wait定時(shí)器:當(dāng)連接的一方進(jìn)入time-wait狀態(tài)后啟動(dòng)該定時(shí)器,保證連接在time-wait時(shí)間內(nèi)關(guān)閉
實(shí)現(xiàn)中,Tcp的定時(shí)器有兩個(gè):一個(gè)是快速定時(shí)器,一個(gè)是慢速定時(shí)器。這兩個(gè)定時(shí)器被作為基準(zhǔn)定時(shí)器用來(lái)實(shí)現(xiàn)更復(fù)雜的邏輯(大部分是通過(guò)定時(shí)計(jì)數(shù)的辦法來(lái)實(shí)現(xiàn))。
3.1 Tcp的快速定時(shí)器:
該定時(shí)器每隔250毫秒調(diào)用一次。它被用來(lái)發(fā)送延遲的ack
該函數(shù)遍歷所有處于active狀態(tài)的pcb,如果pcb的ack_delay標(biāo)識(shí)被設(shè)置,一個(gè)空tcp確認(rèn)段會(huì)被發(fā)送,之后,標(biāo)識(shí)就被清除。
3.2 Tcp的慢速定時(shí)器:
該定時(shí)器沒(méi)500毫秒調(diào)用一次。該定時(shí)器實(shí)現(xiàn)了tcp的超時(shí)重傳定時(shí)器,以及用來(lái)移除處在time-wait狀態(tài)足夠長(zhǎng)時(shí)間的pcb的定時(shí)器。它還被用來(lái)增加每個(gè)pcb中的類似于?;钣?jì)數(shù)等一些計(jì)數(shù)變量
在該定時(shí)器中,會(huì)掃描未確認(rèn)段組成的鏈(由tcp_seg結(jié)構(gòu)體中的unacked指針指向),當(dāng)未確認(rèn)段所在的pcb的超時(shí)量大于設(shè)定的值時(shí),超時(shí)重傳就會(huì)發(fā)生。
對(duì)于處在TIME-WAIT狀態(tài)中的連接,coarse grained timer也增加PCB結(jié)構(gòu)體中的tmr域。當(dāng)計(jì)數(shù)器達(dá)到2*MSL閥值后連接就被移除。
Coarse grained timer同樣增加全局TCP時(shí)鐘——tcp_ticks,該時(shí)鐘在輪回時(shí)間評(píng)估和重傳超時(shí)中使用。
該函數(shù)的具體邏輯如下:
{
? ?首先增加全局的tcp時(shí)鐘—tcp_ticks。
? ?掃描所有的處于active狀態(tài)的pcb,進(jìn)行相應(yīng)的處理。
? ?掃描遍歷處于time-wait狀態(tài)的pcb,進(jìn)行相應(yīng)的處理。
? ?//因?yàn)樘幵趌isten狀態(tài)的pcb都是在等待對(duì)端的連接,不需要超時(shí)處理。
}
3.2.1 active狀態(tài)pcb的超時(shí)處理
初始化pcb是否需要移除的變量 pcb_remove
如果當(dāng)前pcb的狀態(tài)是已發(fā)送syn(SYN_SENT),并且重傳次數(shù)達(dá)到了syn最大的重傳次數(shù)(TCP_SYNMAXRTX),則增加pcb_remove變量
否則,如果重傳次數(shù)達(dá)到了tcp最大重傳次數(shù)(TCP_MAXRTX),就增加pcb_remove變量。此時(shí)應(yīng)該是某個(gè)ack的最大重傳次數(shù)
如果上述兩個(gè)條件都不滿足:
該pcb的重傳定時(shí)器增加500個(gè)滴答。
如果該pcb的未確認(rèn)段隊(duì)列不為空,也就是說(shuō)存在未確認(rèn)的段,并且上述重傳定時(shí)器計(jì)數(shù)值大于設(shè)定的重傳超時(shí)時(shí)間,說(shuō)明某個(gè)段很可能丟失了,超時(shí)重傳將發(fā)生
{
??? //此時(shí),除非我們處于嘗試連接某個(gè)對(duì)端,否則,將超時(shí)重傳時(shí)間加倍。因?yàn)樘幱谶B接建立階段的超時(shí)時(shí)間是固定的,典型值是75秒。
??? 如果當(dāng)前不是在SYN_SENT狀態(tài),重新計(jì)算超時(shí)重傳時(shí)間。(重傳定時(shí)器的重傳時(shí)間的取值是依賴與連接上測(cè)算到的RTT的)
??? 當(dāng)超時(shí)發(fā)生時(shí),表明發(fā)生了擁塞,tcp要執(zhí)行慢啟動(dòng)和擁塞避免算法。(算法細(xì)節(jié)1234步驟卷一235頁(yè))。首先取擁塞窗口(cwnd)和接收者通告的窗口(snd_wnd)中的小值作為當(dāng)前的窗口,將當(dāng)前的閥值(ssthresh)減小到當(dāng)前窗口的一半,如果閥值小于路徑最大段,則設(shè)置為2個(gè)報(bào)文段的大小。(因?yàn)殚y值是按照段的大小增加的,所以如果不小于一個(gè)的話就至少是兩個(gè))最后設(shè)置當(dāng)前的擁塞窗口為一個(gè)段的大小。
??? 調(diào)用tcp_rexmit_rto()重傳未確認(rèn)隊(duì)列上的第一個(gè)段。
}
//檢查是否有pcb在fin-wait-2狀態(tài)停留了太長(zhǎng)時(shí)間。
如果當(dāng)前pcb是在上述狀態(tài),并且tcp定時(shí)器當(dāng)前的滴答數(shù)tcp_ticks與該pcb中保存的變量tmr(該變量主要用于保存連接最后一次活躍時(shí)的時(shí)間)的差值大于設(shè)定的該狀態(tài)的超時(shí)時(shí)間,則pcb_remove變量加一(該變量在后續(xù)移除不用的pcb時(shí)用到)。
//檢查是否需要發(fā)送?;钐綔y(cè)報(bào)文(KEEPALIVE)。
如果當(dāng)前的連接支持?;钐綔y(cè)選項(xiàng)(SO_KEEPALIVE),并且當(dāng)前的連接處于建立(ESTABLISHED)或者CLOSE-WAIT狀態(tài)
{
??? 如果當(dāng)前時(shí)鐘滴答數(shù)tcp_ticks和pcb中tmr變量的差值大于?;钐綔y(cè)前空閑的時(shí)間和?;钐綔y(cè)報(bào)文發(fā)送消耗的最長(zhǎng)時(shí)間之和,那么說(shuō)明該連接空閑了太長(zhǎng)時(shí)間,該連接不再需要。調(diào)用tcp_abort向?qū)Χ税l(fā)送一個(gè)RST報(bào)文,并釋放與該連接相關(guān)的存儲(chǔ)。
??? 否則,如果該時(shí)間在?;钐綔y(cè)報(bào)文發(fā)送期間,還沒(méi)有達(dá)到?;钐綔y(cè)報(bào)文發(fā)送的最大次數(shù),那么就調(diào)用tcp_keepalive繼續(xù)發(fā)送?;钐綔y(cè)報(bào)文,增加該pcb上的保活探測(cè)報(bào)文發(fā)送計(jì)數(shù)變量keep_cnt。
}
//如果該連接上保存有序號(hào)外的數(shù)據(jù),并且有很長(zhǎng)時(shí)間不再活躍了,則將序號(hào)外的數(shù)據(jù)丟棄(這些數(shù)據(jù)如果最終需要傳輸?shù)脑掃€是會(huì)通過(guò)重傳來(lái)完成的)
如果該連接的序號(hào)外段數(shù)據(jù)隊(duì)列不為空,并且當(dāng)前時(shí)鐘滴答數(shù)和tmr的差值大于等于設(shè)定的TCP_OOSEQ_TIMEOUT,也就是說(shuō)該連接空閑了這么長(zhǎng)時(shí)間,則調(diào)用tcp_seq_free釋放序號(hào)外段數(shù)據(jù),同時(shí)隊(duì)列也被設(shè)置為null。
//檢查確認(rèn)當(dāng)前連接是否在SYN-RECVD狀態(tài)停留了太長(zhǎng)時(shí)間
如果當(dāng)前pcb的狀態(tài)是SYN-RECVD,并且當(dāng)前的空閑時(shí)間大于設(shè)定的TCP_SYN_RECVD_TIMEOUT,則增加pcb_remove變量,在后續(xù)處理中會(huì)將該pcb從鏈表上移除。
//檢查確認(rèn)當(dāng)前連接是否在LAST-ACK狀態(tài)停留了太長(zhǎng)時(shí)間
如果當(dāng)前pcb的狀態(tài)是LAST_ACK,并且連接空閑時(shí)間大于2MSL,就增加變量pcb_remove。這有點(diǎn)類似與TIME-WAIT狀態(tài)。說(shuō)明即使是服務(wù)器,如果對(duì)端主動(dòng)關(guān)閉了連接,但是服務(wù)應(yīng)用沒(méi)有關(guān)閉該連接,我們也不會(huì)停留很長(zhǎng)時(shí)間,最長(zhǎng)2MST后服務(wù)器端也就會(huì)關(guān)閉該連接。
//在這一部分的最后,我們根據(jù)pcb_remove變量的設(shè)置情況來(lái)對(duì)需要移除的pcb進(jìn)行移除操作,并進(jìn)行其他相關(guān)操作。
//雖然在之前我們將該變量多次進(jìn)行自加,但是實(shí)際上該變量大于1的情況并不多,因?yàn)樯鲜雠袛鄺l件有好多是互斥的,所以在某個(gè)添加滿足的情況下,其他許多都是跳過(guò)的。
如果該變量為真,說(shuō)明需要移除該pcb。首先調(diào)用tcp_pcb_purge釋放與該pcb相關(guān)的存儲(chǔ)。將該pcb從active pcb鏈表上移除,釋放其占用的內(nèi)存,并將pcb指針指向下一個(gè)pcb塊。
否則,說(shuō)明沒(méi)有需要移除的pcb。增加polltmr變量,如果poll時(shí)間大于設(shè)定的周期,則復(fù)位該變量。執(zhí)行注冊(cè)的poll函數(shù),如果該函數(shù)正確執(zhí)行了,則調(diào)用tcp_output看看該連接上是否有需要發(fā)送的數(shù)據(jù),如果有的話就發(fā)送,即使是一個(gè)單純的ack。
將pcb指針移到下一個(gè)pcb上
如果新的pcb指針不為空,也就是說(shuō)active鏈表上還有未處理的pcb,則繼續(xù)回到active pcb處理的開始處進(jìn)行下一次處理。
這樣,我們處理了在active狀態(tài)的pcb,并且涉及到tcp的SYN_RECVD? SYN_SENT? ESTABLISHED? CLOSE_WAIT? LAST_ACK? FIN_WAIT_2狀態(tài)。從tcp的狀態(tài)機(jī)來(lái)看,需要處理的狀態(tài)就剩余CLOSING? TIME_WAIT? FIN_WAIT_1狀態(tài)了。對(duì)于?????? CLOSING? FIN_WAIT_1狀態(tài)不需要特殊處理。最后就剩余下面要處理的TIME_WAIT狀態(tài)了。
3.2.2 time-wait狀態(tài)pcb的超時(shí)處理
到這里就處理了active鏈表上的所有pcb,下面繼續(xù)處理time_wait鏈表上的pcb。這部分的處理很簡(jiǎn)單,因?yàn)樵撴湵砩系乃衟cb的狀態(tài)都是time-wait的,所以只是進(jìn)行2MSL超時(shí)的檢查。
首先,將pcb_remove變量復(fù)位
如果當(dāng)前連接空閑的時(shí)間大于2MSL,則將pcb_remove變量加一。
如果該變量被增加了,那么就釋放與該pcb相關(guān)的存儲(chǔ),并將該pcb從鏈表上移除,釋放其占用的內(nèi)存。最后使其指向鏈表中的下一個(gè)pcb。
否則,說(shuō)明該連接在time_wait狀態(tài)還沒(méi)有超時(shí),不采取任何操作,只是將指針下移。
同樣,如果新的pcb指針不為空,說(shuō)明time_wait狀態(tài)鏈表上還有未處理的pcb,接著返回的該處理的開始繼續(xù)處理下一個(gè)time_wait狀態(tài)的pcb。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-405721.html
接續(xù):Lwip之TCP協(xié)議實(shí)現(xiàn)(二)_龍赤子的博客-CSDN博客文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-405721.html
到了這里,關(guān)于Lwip之TCP協(xié)議實(shí)現(xiàn)(一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!