tcp詳解卷123全第27章的函數(shù)_第1頁
tcp詳解卷123全第27章的函數(shù)_第2頁
tcp詳解卷123全第27章的函數(shù)_第3頁
tcp詳解卷123全第27章的函數(shù)_第4頁
tcp詳解卷123全第27章的函數(shù)_第5頁
已閱讀5頁,還剩20頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、第27章TCP的函數(shù)27.1引言本章介紹多個(gè)TCP函數(shù),它們?yōu)橄聝烧逻M(jìn)一步討論TCP的輸入打下了基礎(chǔ): tcp_drain是協(xié)議的任何處理。耗盡處理函數(shù),當(dāng)內(nèi)核的 mbuf用被調(diào)用。實(shí)際上,不做 tcp_dropRST來丟棄連接。 tcp_close執(zhí)行正常的TCP連接關(guān)閉操作:FIN,并等待協(xié)議要求的 4次報(bào)文交換以終止連接。卷1的18.2節(jié)討論了連接關(guān)閉時(shí)雙方需要交換的 4個(gè)報(bào)文。 tcp_mss處理收到的MSS選項(xiàng),并在TCP的MSS選項(xiàng)時(shí)計(jì)算應(yīng)填入的MSS值。 t c p _ c t l i n p u t在收到對應(yīng)于某個(gè) TCP報(bào)文段的ICMP差錯(cuò)時(shí)被調(diào)用,它接著調(diào)用tcp_noti

2、fy處理ICMP差錯(cuò)。tcp_quench專門負(fù)責(zé)處理ICMP的源站抑制差錯(cuò)。 TCP_REASS宏和tcp_reass函數(shù)管理連接重組隊(duì)列中的報(bào)文段。重組隊(duì)列處理收到的 亂序報(bào)文段,某些報(bào)文段還可能互相重復(fù)。 tcp_trace向內(nèi)核的TCP調(diào)試循環(huán)緩存中添加(8)程序可以打印緩存內(nèi)容。(插口選項(xiàng)SO_DEBUG)。運(yùn)行trpt27.2tcp_drain函數(shù)tcp_drain是所有TCP函數(shù)中最簡單的。它是協(xié)議的 pr_drain函數(shù),在內(nèi)核的mbuf用,由m_reclaim調(diào)用。圖10-32中, ip_drain丟棄其重組隊(duì)列中的所有數(shù)據(jù)報(bào)分片,而UDP則不定義的耗盡處理函數(shù)。盡管 TCP

3、也占用mbuf位于接收窗口內(nèi)的亂序報(bào)文段但N e t / 3 實(shí)現(xiàn)的 T C P 并不丟棄這些 m b u f ,即使內(nèi)核的 m b u f 已用完。相反,tcp_drain不做任何處理,假定收到的(但次序差錯(cuò))的TCP報(bào)文段比IP分片重要。27.3tcp_drop函數(shù)tcp_drop在整個(gè)系統(tǒng)中多次被調(diào)用,RST報(bào)文段以丟棄連接,并用進(jìn)程返回差錯(cuò)。它與關(guān)閉連接(tcp_disconnect函數(shù))不同,后者遷圖所規(guī)定的連接終止步驟。圖27-1列出了調(diào)用tcp_drop的7種情況和相應(yīng)的errno參數(shù)。圖27-2給出了tcp_drop函數(shù)。2 0 2 - 2 1 3 如果TCP收到了一個(gè)SYN,

4、連接被同步,則必須FIN,并遵守TCP狀態(tài)變RST。tcp_drop把狀態(tài)設(shè)為CLOSED,并調(diào)用tcp_output。從圖24-16可知,CLOSED狀態(tài)的tcp_outflags數(shù)組中包含RST標(biāo)志。2 1 4 - 2 1 6如果errno等于ETIMEDOUT,且連接上曾收到過軟差錯(cuò) (如EHOSTUNREACH),第 27章 TCP的函數(shù)713軟差錯(cuò)代碼將取代內(nèi)容不確定的ETIMEDOUT,做為返回的插口差錯(cuò)。2 1 7tcp_close結(jié)束插口關(guān)閉操作。圖27-1調(diào)用tcp_drop函數(shù)和errno 參數(shù)圖27-2 tcp_drop 函數(shù)27.4tcp_close函數(shù)通常情況下,如果

5、應(yīng)用進(jìn)程關(guān)閉,且在LAST_ACK狀態(tài)時(shí)收到了ACK,tcp_input將調(diào)用tcp_close關(guān)閉連接;或者當(dāng) 2 MSL定時(shí)器超時(shí),插口從 TIME_WAIT狀態(tài)變遷到CLOSED狀態(tài)時(shí), tcp_timers也會調(diào)用tcp_close。它也可以在其他狀態(tài)被調(diào)用,一種可能是發(fā)生了差錯(cuò),如上一小節(jié)討論過的情況。 tcp_close連接占用的內(nèi)存(IP和TCP首部模板、TCP性。塊、Internet PCB和保存在連接重組隊(duì)列中的所有亂序報(bào)文段 ),并更新特我們分3部分講解這個(gè)函數(shù),前兩部分討論特性,最后一部分介紹。27.4.1特性rt_metrics結(jié)構(gòu)(圖18 - 26 )中保存了 9個(gè)變

6、量,有 6個(gè)用于TCP。其中8個(gè)變量可通過函數(shù)errno描述tcp_inputENOBUFS服務(wù)器收到SYN,但內(nèi)核無法為t_template分配所需的mbuftcp_inputECONNREFUSED收到的RST是對本地的SYN的響應(yīng)tcp_inputECONNRESET在現(xiàn)存連接上收到了RSTtcp_timersETIMEDOUT重傳定時(shí)器連續(xù)超時(shí)13次,仍未收到對端的ACK(圖25-25)tcp_timersETIMEDOUT連接建立定時(shí)器超時(shí)(圖25-16),或者定時(shí)器超時(shí),且連續(xù)9次窗口探測報(bào)文段,對方均無響應(yīng)tcp_usrreqECONNABORTEDPRU_ABORT請求tcp_

7、usrreq0關(guān)閉插口,設(shè)定SO_LINGER選項(xiàng),且拖延時(shí)間為0714TCP/IP詳解 卷2:實(shí)現(xiàn)route (8)命令讀寫(第9個(gè), rmx_pksent未使用):圖27-3列出了這些變量。此外,運(yùn)行route命令時(shí),加入-lock選項(xiàng),可以設(shè)置rmx_locks成員變量(圖20-13)中對應(yīng)的RTV_比特,告訴內(nèi)核不要更新對應(yīng)的參數(shù)。關(guān)閉TCP 插口時(shí),如果下列條件滿足:連接上傳輸?shù)臄?shù)據(jù)量足夠生成有效的統(tǒng)計(jì)值,并且變量未被鎖定, tcp_close將更新3個(gè)均偏差估計(jì)器和慢起動門限。參數(shù) 已平滑的RTT估計(jì)器、已平滑的RTT平圖27-3 TCP用到的rt_metrics 結(jié)構(gòu)中的變量圖2

8、7-4給出了tcp_close的第一部分。1.2 3 4 - 2 4 8接上已是否默認(rèn)的了足夠的數(shù)據(jù)量緩存大小為 8192字節(jié)(sb_hiwat),因此首先比較初始序號和連的最大序號,測試是否已傳輸了 131 072 字節(jié)(16個(gè)完整的緩存)的數(shù)據(jù)。此外,插口還必須有一條非默認(rèn)的緩存(參見習(xí)題19.2)。請注意,如果傳輸?shù)臄?shù)據(jù)量在 N×232(N>1)和N×232+131072(N>1)之間,則因?yàn)樾蛱柨赡芑乩@,比較時(shí)也許會出現(xiàn)問題,盡管可能性不大。但目前很少有連接會傳輸4 G的數(shù)據(jù)。盡管Internet上存在大量的默認(rèn),緩存對于維護(hù)有效的表還是很有用的。如果主

9、機(jī)長期與另外某個(gè)主機(jī) (或網(wǎng)絡(luò))交換數(shù)據(jù),即使默認(rèn)可用,也應(yīng)運(yùn)行route命令向表中添加源站選路和目的選路的,從而在整條連接上維護(hù)有效的信息(參見習(xí)題19.2)。這些信息在系統(tǒng)重啟時(shí)丟失。2 5 0 管理員可以鎖定圖27-3中的變量,防止內(nèi)核修改它們。因此,代碼在更新這些變量之前, 必須先檢查其鎖定狀態(tài)。2. 更新RTT2 5 1 - 2 6 4 t_srtt的為8個(gè)滴答(圖25-19),而rmx_rtt的為微秒。因此,首先必須實(shí)現(xiàn)換算, t_srtt乘以1 000 000(RTM_RTTUNIT),除以2(滴答/秒)再乘以8,得到RTT 的最新值。如果rmx_rtt值已存在,它被更新為最新值

10、與原有值和的一半,即兩者的平均值。如果不存在,最新值將直接賦給rmx_rtt變量。3. 更新平均偏差2 6 5 - 2 7 3更新平均偏差的算法與更新RTT的類似,也需要把為4個(gè)滴答的t_rttvar換算為以微秒為。rt_metrics成員tcp_close是否保存該成員tcp_mss是否使用該成員route(8)附加參數(shù)rmx_expire rmx_hopcount rmx_mtu rmx_recvpipe rmx_rtt rmx_rttvar rmx_sendpipe rmx_ssthresh-expire-hopcount-mtu-recvpipe-rtt-rttvar-sendpipe

11、-ssthreshTCP的函數(shù)715第 27章圖27-4 tcp_close 函數(shù):更新RTT和平均偏差圖27-5給出了tcp_close的下一部分代碼,更新的慢起動門限。2 7 4 - 2 8 3 滿足下列條件時(shí),慢起動門限被更新: (1)它被更新過(rmx_ssthresh非零);(2)管理員規(guī)定了rmx_sendpipe,而snd_ssthresh的最新值小于rmx_sendpipe的一半。如同代碼注釋中指出的, TCP更新rmx_ssthresh值,除非因?yàn)閿?shù)據(jù)分組丟失而不得不716TCP/IP詳解 卷2:實(shí)現(xiàn)這樣做。從這個(gè)角度出發(fā),除非十分必要, TCP修改門限值。圖27-5tcp_

12、close 函數(shù):更新慢起動門限2 8 4 - 2 9 0變量snd_ssthresh以字節(jié)為,除以MSS(t_maxseg)得到報(bào)文段數(shù),加上1/2t_maxseg是為了保證總報(bào)文段容量必定大于 snd_ssthresh字節(jié)。報(bào)文段數(shù)的下限為 2個(gè)報(bào)文段。2 9 1 - 2 9 7MSS加上IP和TCP首部大小( 40 ),再乘以報(bào)文段數(shù),利用得到的結(jié)果來更新rmx_ssthresh,采用的算法與圖27-4中的相同(新值的1/2加上原有值的1/2)。27.4.2圖27-6給出了tcp_close的最后一部分,插口占用的內(nèi)存。圖27-6 tcp_close 函數(shù):連接第 27章 TCP的函數(shù)7

13、17圖27-6 (續(xù))1.重組隊(duì)列占用的mbuf2 9 9 - 3 0 6如果連接重組隊(duì)列中還有報(bào)文段,則丟棄它們。重組隊(duì)列用于存放收到位于接收 窗口內(nèi)、但次序差錯(cuò)的報(bào)文段。在等待接收的正常序列報(bào)文段到達(dá)之前,它們會一直保存在重組隊(duì)列中;之后,報(bào)文段被重組并遞交給應(yīng)用程序。 27.9節(jié)會詳細(xì)討論這一過程。2.3 0 7 - 3 0 9首部模板和TCP調(diào)用 m _ f r e e塊I P和T C P首部模板,調(diào)用 f r e eT C P塊,調(diào)用sodisconnected3.PCBPRU_DISCONNECT請求,標(biāo)記插口已斷開連接。3 1 0 - 3 1 8如果插口的Internet PCB

14、保存在TCP的高速緩存中,則把TCP的PCB鏈表表頭賦給tcp_last_inpcb,以清空緩存。接著調(diào)用in_pcbdetachPCB占用的內(nèi)存。27.5tcp_mss函數(shù)tcp_mss被兩個(gè)函數(shù)調(diào)用:1) tcp_output,準(zhǔn)備SYN時(shí)調(diào)用,以添加MSS選項(xiàng);2) tcp_input,收到的SYN報(bào)文段中包含MSS選項(xiàng)時(shí)調(diào)用;tcp_mss函數(shù)檢查到達(dá)目的地的緩存,計(jì)算用于該連接的 MSS。圖27-7給出了tcp_mss第一部分的代碼,如果 PCB中沒有到達(dá)目的地的,則設(shè)法得到所需的。圖27-7 tcp_mss 函數(shù):如果PCB中沒有,則設(shè)法得到所需718TCP/IP詳解 卷2:實(shí)現(xiàn)圖

15、27-7 (續(xù))1. 如果需要,就獲取1 3 9 1 - 1 4 1 7 如果插口沒有高速緩存,則調(diào)用rtalloc得到一條。與外出相關(guān)的接口指針法得到所需在ifp中。外出接口是非常重要的,因?yàn)槠?MTU會影響TCP通告的MSS。如果無,函數(shù)就立即返回默認(rèn)值 512 ( tcp_mssdflt)。圖27-8給出了tcp_mss的下一部分代碼,得到的是否有相應(yīng)的參數(shù)表。如果有,則變量t_rttmin、t_srtt和t_rttvar將初始化為參數(shù)表中的對應(yīng)值。圖27-8 tcp_mss 函數(shù):是否有相應(yīng)的RTT參數(shù)表2. 初始化已平滑的RTT估計(jì)器1 4 2 0 - 1 4 3 2如果連接上不存在

16、RTT樣本值(t_srtt=0),并且rmx_rtt非零,則將后者賦第 27章 TCP的函數(shù)719給已平滑的RTT估計(jì)器t_srtt。如果參數(shù)表鎖定標(biāo)志的 RTV_RTT比特置位,表明連接的最小 RT T ( t _ r t t m i n ) 也應(yīng)初始化為 r m x _ r t t 。前面介紹過, t c p _ n e w t c p c b 把t_rttmin初始化為2個(gè)滴答。rmx_rtt(以微秒為)轉(zhuǎn)換為t_srtt(以8個(gè)滴答為),這是圖27-4的反變換。注意,t_rttmin等于t_srtt的1/8,因?yàn)榍罢邲]有除以縮放因子TCP_RTT_SCALE。3. 初始化已平滑的RTT

17、平均偏差估計(jì)器1 4 3 3 - 1 4 3 9 如果的rmx_rttvar(以微秒為)值非零,將其轉(zhuǎn)換為t_rttvar (以4個(gè)滴答為)。但如果為零,則t_rttvar等于t_rtt,即偏差等于均值。已平滑的 RTT平均偏差估計(jì)器默認(rèn)設(shè)置為±1 RTT。由于t_rttvar的為4個(gè)滴答,而t_rtt的為8個(gè)滴答,t_srtt值也必須做相應(yīng)轉(zhuǎn)換。4. 計(jì)算初始RTO1 4 4 0 - 1 4 4 2 計(jì)算當(dāng)前的RTO,并在t_rxtcur中,采用下列算式更新:RTO=srtt+2×rttvar計(jì)算第一個(gè)RTO 時(shí),乘數(shù)取2,而非4,上式與圖25-21中用到的算式相同。將縮

18、放關(guān)系代入,得到:t_srtt + t_rttvart_srttt_rttvar=4RTO =+ 2 ´842即為TCPT_RANGESET的第二個(gè)參數(shù)。圖27-9給出了tcp_mss的下一部分,計(jì)算MSS。圖27-9 tcp_mss 函數(shù):計(jì)算mss5. 從表中的MTU得到MSS1 4 4 4 - 1 4 5 0如果表中的MTU有值,則將其賦給mss。如果沒有,則mss初始值等于外出接口的MTU值減去40(IP和TCP首部默認(rèn)值)。對于以太網(wǎng), MSS初始值應(yīng)為1460。6. 減小MSS,等于MCLBYTES的倍數(shù)1 4 5 1 - 1 4 5 7如 果 m s s大 于 M C

19、L B Y T E S , 則 減 小 m s s的 值 ,等 于 最 接 近 的720TCP/IP詳解 卷2:實(shí)現(xiàn)MCLBYTES(mbuf簇大小)的整數(shù)倍。如果MCLBYTES值(通常等于1024或2048)與MCLBYTES值 減1邏輯與后等于0,說明MCLBYTES等于2的倍數(shù)。例如, 1024(0x400)邏輯與1023(0x3ff) 等于0。代碼通過清零 mss的若干低位比特,將 mss減小到最接近的 MCLBYTES的倍數(shù):如果mbuf簇大小為1024,mss與1023的二進(jìn)制補(bǔ)碼(0xfffffc00)邏輯與,低位的10 bit被清零。對于以太網(wǎng), m s s 將從 1 4 6

20、 0 減至 1 0 2 4 。如果 m b u f簇大小為 2 0 4 8 ,與 2 0 4 7 的二進(jìn)制補(bǔ)碼(0xffff8000)邏輯與,低位的 11 bit被清零。對于令牌環(huán), MTU大小為4464,上述運(yùn)算將mss從4424減為4096。如果MCLBYTES不是2的倍數(shù),代碼用mss整數(shù)除以MCLBYTES后,再乘上MCLBYTES,從而將mss減小到最接近的MCLBYTES的倍數(shù)。7.目的地是本地地址還是遠(yuǎn)端地址1 4 5 8 - 1 4 5 9如 果 目 的 I P不 是 本 地 地 址 ( i n _ l o c a l a d d r 返 回 零 ), 且 m s s 大于51

21、2(tcp_mssdflt),則將mss設(shè)為512。IP地址是否地地址取決于全局變量 subnetsarelocal,內(nèi)核編譯時(shí)把符號變量SUBNETSARELOCAL的值賦給它。默認(rèn)值為1,意味著如果給定IP地址與主機(jī) 任一接口的IP地址具有相同的網(wǎng)絡(luò)ID,則被認(rèn)為是一個(gè)本地地址。如果為 0,則給定IP地址必須與主機(jī)任一接口的IP地址具有相同的網(wǎng)絡(luò)號和子網(wǎng)號,才會被認(rèn)為是一個(gè)本地地址。對于非本地地址,將MSS最小化是為了避免IP數(shù)據(jù)報(bào)經(jīng)廣域網(wǎng)時(shí)被分片。絕大多數(shù)WAN鏈路的MTU只有1006,這是從ARPANET遺留下來的一個(gè)問題。在卷 1的11.7節(jié)中討論過,現(xiàn)代的多數(shù)WAN支持1500,甚

22、至更大的MTU。感的讀者還可閱讀卷1的24.2節(jié)中討論的MTU發(fā)現(xiàn)特性(RFC 1191,Mogul and Deering 1990)。Net/3不支持MTU發(fā)現(xiàn)。圖27-10給出了tcp_mss最后一部分的代碼。8. 對端的MSS用作上限1 4 6 1 - 1 4 7 2 如果tcp_mss被tcp_input調(diào)用,參數(shù)offer非零,等于對端通告的mss值。如果mss大于對端通告的值,則將offer賦給它。例如,如果函數(shù)計(jì)算得到的 mss等于1024, 但對端通告的值只有 512,則mss必須被設(shè)定為512。相反,如果mss等于536(即輸出MTU等于576),而對端通告的值為 1460

23、,TCP仍舊使用536。只要不超過對端通告的值, mss可以取小于它的任何一個(gè)值。如果 tcp_mss被tcp_output調(diào)用, offer等于0,用于項(xiàng)。注意,盡管mss的上限可變,其下限固定為32。MSS選1 4 7 3 - 1 4 8 3 如果mss小于tcp_newtcpcb中設(shè)定的默認(rèn)值t_maxseg(512),或者如果TCP正在處理收到的MSS選項(xiàng)(offer非零),則需執(zhí)行下列步驟。首先,如果的rmx_sendpipe有值,則采用它做為緩存的高端 (high-water)標(biāo)志(圖16-4)。如果緩存小于mss,則使用較小的值。除非是應(yīng)用程序有意把緩存定得很小,或者管理員將 r

24、mx_sendpipe定得很小,發(fā)生,因?yàn)榫彺娴纳舷弈J(rèn)值為 8192,大于絕大多數(shù)的mss。這種情況一般9. 增加緩存大小,等于最近的MSS整數(shù)倍1 4 8 4 - 1 4 8 9增加緩存大小,262 144 ,即256×1024)。插口等于最近的 mss整數(shù)倍,上限為sb_max(Net/3中定義為緩存的上限設(shè)定為sbreserve。例如,上限默認(rèn)值等于第 27章 TCP的函數(shù)7218192,但對于以太網(wǎng)上的本地TCP傳輸,其mbuf簇大小為2048(假定mss等于1460),代碼把上限值增加到8760(等于6×1460)。但對于非本地的連接, mss等于512,上限值

25、保持8192不變。圖27-10 tcp_mss 函數(shù):結(jié)束處理1 4 9 0由于t_maxseg已小于默認(rèn)值(512),或者由于收到了對端的MSS選項(xiàng),所以722TCP/IP詳解 卷2:實(shí)現(xiàn)新它。1 4 9 1 - 1 4 9 9 對接收緩存的處理與緩存相同。10. 初始化擁塞窗口和慢起動門限1 5 0 0 - 1 5 0 9擁塞窗口的值, snd_cwnd,等于一個(gè)最大報(bào)文段長度。如果表中的rmx_ssthresh非零,慢起動門限(snd_ssthresh)初始化為該值,但應(yīng)保證其下限為兩個(gè) 最大報(bào)文段長度。1 5 1 0函數(shù)最后返回mss。tcp_input忽略這一返回值(圖28-10,因

26、為它已收到對端的MSS選項(xiàng)),但圖26-23中,tcp_output將它用作MSS通告。舉例下面通過接建立的實(shí)例說明 tcp_mss的操作過程。連接建立過程中,它會被調(diào)用兩次:SYN時(shí)和收到對端帶有MSS選項(xiàng)的SYN時(shí)。1) 創(chuàng)建插口, tcp_newtcpcb初始化t_maxseg為512。2) 應(yīng)用進(jìn)程調(diào)用 connect。為了在SYN報(bào)文段中加入 MSS選項(xiàng), tcp_output調(diào)用tcp_mss,參數(shù)offer等于零。假定目的IP地以太網(wǎng)地址, mbuf簇大小為2048,執(zhí)行圖27-9中的代碼后, mss等于1460。由于offer等于零,圖27-10中的代碼不修改mss值,函數(shù)返回

27、 1 4 6 0 。因?yàn)?1 4 6 0 大于默認(rèn)值 ( 5 1 2 ) 而且未收到對端的 M S S 選項(xiàng),緩存大小不變。tcp_output MSS選項(xiàng),通告MSS大小為1460。3) 對端 響應(yīng)SYN,通告mss大小為1024。tcp_input調(diào)用tcp_mss,參數(shù)offer 等于1024。圖27-9的代碼邏輯仍舊設(shè)定mss為1460,但在圖27-10起始處的min語句將mss減 小為1024。因?yàn)閛ffer非零,緩存大小增加至最近的 1024的整數(shù)倍(等于8192)。t_maxseg 更新為1024。初看上去, tcp_mss的邏輯存在問題: TCP通告mss大小為1460,之后從

28、對端收到的mss只有1024。盡管TCP只能1024字節(jié)的報(bào)文段,對端卻能夠1460字節(jié)的報(bào)文段。讀者可能會認(rèn)為緩存應(yīng)等于 1024的倍數(shù),而接收緩存則應(yīng)等于1460的倍數(shù)。但圖27-10中的代碼卻將兩個(gè)緩存大小都設(shè)為對端通告的 mss的倍數(shù)。這是因?yàn)楸M管TCP通告mss為1460,但對端通告的mss僅為1024,對端有可能不會1460字節(jié)的報(bào)文段,而將報(bào)文段限制為 1024字節(jié)。27.6 tcp_ctlinput函數(shù)回想圖22-32中,tcp_ctlinput處理5種類型的ICMP差錯(cuò):目的地不可達(dá)、數(shù)據(jù)報(bào)參數(shù) 錯(cuò)、源站抑制、數(shù)據(jù)報(bào)超時(shí)和重定向。所有重定向差錯(cuò)會上交給相應(yīng)的 TCP或UDP進(jìn)

29、行處理。對于其他4種差錯(cuò),僅當(dāng)它們是被 TCP報(bào)文段的,才會調(diào)用 tcp_ctlinput進(jìn)行處理。圖27-11給出了tcp_ctlinput函數(shù),它與圖23-30的udp_ctlinput函數(shù)類似。3 6 5 - 3 6 6在邏輯上, tcp_ctlinput與udp_ctlinput的唯一區(qū)別是如何處理ICMP源站抑制差錯(cuò)。因?yàn)閕netctlerrmap等于0,UDP忽略源站抑制差錯(cuò)。 TCP檢查源站抑制差錯(cuò), 并把notify函數(shù)的默認(rèn)值tcp_notify改為tcp_quench。TCP的函數(shù)723第 27章圖27-11 tcp_ctlinput 函數(shù)27.7tcp_notify函數(shù)t

30、cp_notify被tcp_ctlinput調(diào)用,處理目的地不可達(dá)、數(shù)據(jù)報(bào)參數(shù)錯(cuò)、數(shù)據(jù)報(bào)超時(shí) 和重定向差錯(cuò)。與UDP的差錯(cuò)處理函數(shù)相比,它要復(fù)雜得多,因?yàn)?TCP必須靈活地處理連接上收到的各種軟差錯(cuò)。圖27-12給出了tcp_motify函數(shù)。圖27-12 tcp_notify 函數(shù)724TCP/IP詳解 卷2:實(shí)現(xiàn)圖27-12 (續(xù))3 2 8 - 3 4 5如果連接狀態(tài)為ESTABLISHED,則忽略EHOSTUNREACH、ENETUNREACH和EHOSTDOWN差錯(cuò)代碼。處理這3個(gè)差錯(cuò)是4.4BSD中新增的功能。 Net/2及早期版本在連接的軟差錯(cuò)變量(t_softerror)中這些

31、差錯(cuò),如果連接最終失敗,則用進(jìn)程返回相應(yīng)的差錯(cuò)碼。回想一下, tcp_xmit_timer在收到一個(gè)ACK,確認(rèn)未復(fù)位t_softerror為零。過的報(bào)文段時(shí),3 4 6 - 3 5 3如果連接還未建立,而且TCP已經(jīng)至少4次重傳了當(dāng)前報(bào)文段, t_softerror中已存在差錯(cuò),則最新的差錯(cuò)將被保存在插口的 so_error變量中,從而應(yīng)用進(jìn)程可以調(diào)用select對插口進(jìn)行讀寫。如果上述條件不滿足,當(dāng)前差錯(cuò)將仍舊保存在 t_softerror中。我們在 t c p _ d r o p 函數(shù)中討論過,如果連接最終由于超時(shí)而被丟棄,t c p _ d r o p 會把t_softerror賦給插

32、口差錯(cuò)變量errno。任何在插口上等待接收或被喚醒,并得到相應(yīng)的差錯(cuò)代碼。數(shù)據(jù)的應(yīng)用進(jìn)程會27.8tcp_quench函數(shù)tcp_quench的函數(shù)代碼在圖27-13中給出。TCP在兩種情況下調(diào)用它:當(dāng)連接上收到源 站抑制差錯(cuò)時(shí),由 t c p _ i n p u t 調(diào)用。當(dāng) i p _ o u t p u t 返回E N O B U F S 差錯(cuò)代碼時(shí),由tp_output調(diào)用。圖27-13 tcp_quench 函數(shù)擁塞窗口設(shè)定為最大報(bào)文段長度,強(qiáng)迫T C P 執(zhí)行慢起動。慢起動門限不變( 與t c p _ t i m e r s處理重傳超時(shí)的思想相同 ),因此,窗口大小將成指數(shù)地增加,

33、直至達(dá)到snd_ssthresh門限或發(fā)生擁塞。27.9TCP_REASS宏和tcp_reass函數(shù)TCP報(bào)文段有可能亂序到達(dá),因此,在數(shù)據(jù)上交給應(yīng)用進(jìn)程之前, TCP必須設(shè)法恢復(fù)正確725第 27章 TCP的函數(shù)的報(bào)文段次序。例如,如果接收方的接收窗口大小為4096,等待接收的下一個(gè)序號為 0。收到的第一個(gè)報(bào)文段攜帶0 1023字節(jié)的數(shù)據(jù)(次序正確),第二個(gè)報(bào)文段攜帶了 2048 3071 字節(jié)的數(shù)據(jù),很明顯,第二個(gè)報(bào)文段到達(dá)的次序差錯(cuò)。如果亂序報(bào)文段位于接收窗口內(nèi), TCP并不丟棄它,而是將其保存在連接的重組隊(duì)列的報(bào)文段 ( 攜帶1024 中,繼續(xù)等待中間16字節(jié)(未用)2047字節(jié)的報(bào)文

34、段 )。這一節(jié)討論處理T C P重組隊(duì)列的代碼,為后兩章討論 t c p _ input打下基礎(chǔ)。如果假定某個(gè)mbuf中包含IP首部、TCP首部和 4 字節(jié)的用戶數(shù)據(jù) ( 回想圖 2 - 1 4 的左半部分),如圖27-14所示。此外還假定數(shù)據(jù)的序號依次為7、8、9和10。圖24 - 12中定義的tcpiphdr結(jié)構(gòu)里包含了ipovly和tcphdr兩個(gè)結(jié)構(gòu), tcphdr結(jié)構(gòu)在圖24-12中給出。圖27-14只列出了與重組有 (20字節(jié)) (20字節(jié)) 4字節(jié)數(shù)據(jù) 關(guān)的一些變量: t i _ n e x t 、t i _ p r e v 、40字節(jié)(未用)ti_len、ti_dport和ti

35、_seq。頭兩個(gè)指針指向由給定連接所有亂序報(bào)文段組成的雙向鏈表。鏈表頭保存在連接的 TCP塊中:結(jié)圖27-14 舉例:帶有4字節(jié)數(shù)據(jù)的IP和TCP首部構(gòu) 的 頭 兩 個(gè) 成 員 變 量 為 s e g _ n e x t 和seg_prev。ti_next和ti_prev指針與IP首部的頭8個(gè)字節(jié)重復(fù),只要數(shù)據(jù)報(bào)到達(dá)了 TCP,就不再需要這些內(nèi)容。 ti_len等于TCP數(shù)據(jù)的長度, TCP計(jì)算檢驗(yàn)和之前首先計(jì)算并個(gè)字段。這27.9.1 TCP_REASS宏tcp_input收到數(shù)據(jù)后,就調(diào)用圖 27-15中的宏TCP_REASS,把數(shù)據(jù)放入連接的重組隊(duì)列。TCP_REASS只在一種情況下被調(diào)

36、用:參見圖 29-22。5 4 - 6 3tp是指向連接TCP塊的指針, ti是指向接收報(bào)文段的tcpiphdr結(jié)構(gòu)的指針。如果下列3個(gè)條件均為真:1) 報(bào)文段到達(dá)次序正確 (序號ti_seq等于連接上等待接收的下一序號, rcv_nxt);并且2) 連接的重組隊(duì)列為空(seg_next指向3) 連接處于ESTABLISHED狀態(tài)。,而不是某個(gè)mbuf);并且則執(zhí)行下列步驟:設(shè)定延遲ACK標(biāo)志;更新rcv_nxt,增加報(bào)文段攜帶的數(shù)據(jù)長度;如果報(bào) 文段TCP首部中FIN標(biāo)志置位,則flags參數(shù)中增加TH_FIN標(biāo)志;更新兩個(gè)統(tǒng)計(jì)值;數(shù)據(jù)放入插口的接收緩存;喚醒所有在插口上等待接收的應(yīng)用進(jìn)程。

37、726TCP/IP詳解 卷2:實(shí)現(xiàn)圖27-15 TCP_REASS 宏:向連接的重組隊(duì)列中添加數(shù)據(jù)必須滿足前述3個(gè)條件的是:首先,如果數(shù)據(jù)次序差錯(cuò),則必須將其放入重組隊(duì)列,直至收到了中間的報(bào)文段,才能把數(shù)據(jù)提交給應(yīng)用進(jìn)程。第二,即使當(dāng)前數(shù)據(jù)到達(dá)次序正確,但如果重組隊(duì)列中已存在亂序數(shù)據(jù),則新的數(shù)據(jù)有可能就是所需的數(shù)據(jù),從而能夠用進(jìn)程同時(shí)提交多個(gè)報(bào)文段中的數(shù)據(jù);第三,盡管請求建立連接的 SYN報(bào)文段中攜帶數(shù)據(jù),但這些數(shù)據(jù)在連接進(jìn)入ESTABLISHED狀態(tài)之前,必須保存在重組隊(duì)列中,不直接提交給應(yīng)用進(jìn)程。6 4 - 6 7如果這3個(gè)條件不是同時(shí)滿足,則TCP_REASS宏調(diào)用TCP_REASS函數(shù)

38、,向重組隊(duì)列中添加數(shù)據(jù)。由于收到的報(bào)文段如果不是亂序報(bào)文段,就有可能是所需的報(bào)文段,因此,置位TF_ACKNOW,要求立即ACK。TCP的一個(gè)重要特性是收到亂序報(bào)文段時(shí),必須立即ACK,這有助于快速重傳算法(29.4節(jié))的實(shí)現(xiàn)。在討論 T C P _ R E A S S 函數(shù)代碼之前,需要先了解圖 2 7 - 1 4 中T C P首部的兩個(gè)端,ti_sport和ti_dport,所起的作用。其實(shí),只要找到了TCP塊并調(diào)用了TCP_REASS,就不再需要它們了。因此, TCP報(bào)文段放入重組隊(duì)列時(shí),可以把對應(yīng) mbuf的地址在這兩在mbuf個(gè)端變量中。對27-14中的報(bào)文段,無需這樣做,因?yàn)?IP

39、和TCP的首部都的數(shù)據(jù)部分,可直接使用dtom宏。但我們在2.6節(jié)討論m_pullup時(shí)曾指出,如果IP和TCP的 首部保存在簇中(如圖2-16所示,對于最大長度報(bào)文這是很正常的 ),dtom宏將無法使用。我們在該節(jié)中到, TCP把從TCP首部指向mbuf的后向指針(back pointer)字段中。在TCP的兩個(gè)端圖27-16舉例說明了這一技術(shù)的用法,利用它處理連接上的兩個(gè)亂序報(bào)文段,每個(gè)報(bào)文段都在一個(gè)mbuf簇中。亂序報(bào)文段雙向鏈表的表頭是連接的 TCP塊中的seg_next成員變量。為簡化起見,圖中未標(biāo)出 seg_prev指針和指向鏈表最后一個(gè)報(bào)文段的 ti_next指針。接收窗口等待接

40、收的下一個(gè)序號為 1(rcv_nxt),但我們假定這個(gè)報(bào)文段丟失了。接著又收到了兩個(gè)報(bào)文段,攜帶 14614380字節(jié)的數(shù)據(jù),這是兩個(gè)亂序報(bào)文段。 TCP調(diào)用m_devget 把它們放入mbuf簇中,如圖2-16所示。TCP的函數(shù)727第 27章(未用)(未用) 后向指針 后向指針 (20字節(jié)) 548字節(jié)(未用)548字節(jié)(未用)圖27-16 兩個(gè)亂序TCP報(bào)文段在mbuf簇中TCP首部的頭32 bit后向指針。指向?qū)?yīng)mbuf的指針,下面介紹的TCP_REASS函數(shù)將用到這個(gè)27.9.2 TCP_REASS函數(shù)圖27-17給出了TCP_REASS函數(shù)的第一部分。參數(shù)包括: tp,指向TCP

41、塊的指針;ti,指向接收報(bào)文段IP和TCP首部的指針; m,指向接收報(bào)文段的 mbuf鏈表的指針。前1460字節(jié)數(shù)據(jù)1460字節(jié)數(shù)據(jù)(20字節(jié))2048字節(jié)簇2048字節(jié)簇728TCP/IP詳解 卷2:實(shí)現(xiàn)面到過, ti既可以指向由m所指向的mbuf的數(shù)據(jù)區(qū),也可以指向一個(gè)簇。圖27-17 TCP_REASS 函數(shù):第一部分后面將看到, TCP收到一個(gè)對SYN的確認(rèn)時(shí), tcp_input將調(diào)用TCP_REASS,并6 9 - 8 3傳遞一個(gè)空的ti指針(圖28-20和圖29-2)。這意味著連接已建立,可以把 SYN報(bào)文段中攜帶的數(shù)據(jù)(TCP_REASS已將其放入重組隊(duì)列 )提交給應(yīng)用程序。連

42、接未建立之前,不這樣做。標(biāo)志“present”位27-23中。8 4 - 9 0遍歷從s e g _ n e x t開始的亂序報(bào)文段雙向鏈表,尋找序號大于接收報(bào)文段序號(ti_seq)的第一個(gè)報(bào)文段。注意, for循環(huán)體中只包含一個(gè)if語句。圖27-18的例子中,文段到達(dá)時(shí)重組隊(duì)列中已有兩個(gè)報(bào)文段。圖中標(biāo)出了指針 q,指向鏈表的下一個(gè)報(bào)文段,帶有字節(jié) 1 0 1 5 。此外,圖中還標(biāo)出了兩個(gè)指針 t i _ n e x t 和ti_prev,起始序號(ti_seq)、長度(ti_len)和數(shù)據(jù)字節(jié)的序號。由于這些報(bào)文段較小,每個(gè)報(bào)文段很可能在單一的mbuf中,如圖27-14所示。鏈表中的前一報(bào)

43、文段文段圖27-18重復(fù)報(bào)文段的重組隊(duì)列舉例TCP的函數(shù)729第 27章圖27-19給出了TCP_REASS下一部分的代碼圖27-19 TCP_REASS函數(shù):第二部分9 1 - 1 0 7如果雙向鏈表中q指向的報(bào)文段前還存在報(bào)文段,則該報(bào)文段有可能與文段重復(fù),因此,挪動指針q,指向q的前一個(gè)報(bào)文段(圖27-18中攜帶字節(jié)48的報(bào)文段),計(jì)算重復(fù)的字節(jié)數(shù),并在變量i中:i = q->ti_seq + q->ti_len - ti->ti_seq;= 4 + 5 -7= 2如果i大于0,則鏈表中原有報(bào)文段與報(bào)文段。如果重復(fù)的字節(jié)數(shù) (i)大于或等于文段攜帶的數(shù)據(jù)間存在重復(fù),如例

44、子中給出的文段的大小,即文段中所有的數(shù)據(jù)都已包含在原有報(bào)文段中,文段是重復(fù)報(bào)文段,應(yīng)予以丟棄。1 0 8 - 1 1 2如果只有部分?jǐn)?shù)據(jù)重復(fù)(如圖27-18所示),m_adj丟棄文段起始i字節(jié)的數(shù)據(jù),文段的序號和長度。挪動 q指針,指向鏈表中的下一個(gè)報(bào)文段。圖 27-20給出并相新了圖27-18中各報(bào)文段和變量此時(shí)的狀態(tài)。1 1 6mbuf的地址m在TCP首部的源端和目的端中,也就是我們前面到的后向指針,防止TCP首部被存放在mbuf簇中,而無法使用dtom宏。宏REASS_MBUF定義為:#define REASS_MBUF(ti) (*(struct mbuf *)&(ti)-&g

45、t;ti_t)ti_t是一個(gè)tcphdr結(jié)構(gòu)(圖24-12),最初的兩個(gè)成員變量是兩個(gè) 16bit的端。請注意占用的 32圖27-19中的注釋“bit空間中?!?,其中隱含了這樣一個(gè)假定,指針能夠存放在兩個(gè)端730TCP/IP詳解 卷2:實(shí)現(xiàn)圖27-20 刪除文段中的字節(jié)7和8后,更新圖27-18圖27-21給出了tcp_reass的第三部分,刪除重組隊(duì)列下一報(bào)文段中可能的重復(fù)字節(jié)。1 1 7 - 1 3 5如果還有后續(xù)報(bào)文段,則計(jì)算文段與下一報(bào)文段間重復(fù)的字節(jié)數(shù),并在變量i中。還是以圖27-18中的報(bào)文段為例,得到:i = 9 + 2 - 10= 1因?yàn)樾蛱?0的字節(jié)同時(shí)存在于兩個(gè)報(bào)文段中。根

46、據(jù)i值的大小,有可能出現(xiàn)3種情況:1) 如果i小于等于0,無重復(fù)。2) 如果i小報(bào)文段的字節(jié)數(shù)(q->ti_len),則有部分重復(fù),調(diào)用m_adj,從該報(bào)文段中丟棄起始的i字節(jié)。3) 如果i大于等報(bào)文段的字節(jié)數(shù),則出現(xiàn)完全重復(fù),從鏈表中刪除該報(bào)文段。1 3 6 - 1 3 9代碼最后調(diào)用insque,把圖27-18中各報(bào)文段和變量此時(shí)的狀態(tài)。文段連接的重組雙向鏈表中。圖 27-22給出了圖27-21 TCP_REASS 函數(shù):第三部分文段鏈表中的前一報(bào)文段TCP的函數(shù)731第 27章圖27-21 (續(xù))圖27-22 丟棄所有重復(fù)字節(jié)后,更新圖 27-20圖27-23給出了tcp_reas

47、s最后一部分的代碼,如果可能,用進(jìn)程遞交數(shù)據(jù)。圖27-23 tcp_reass 函數(shù):第四部分文段鏈表中的前一報(bào)文段732TCP/IP詳解 卷2:實(shí)現(xiàn)1 4 5 - 1 4 6 如果連接還沒有收到SYN(連接處于LISTEN狀態(tài)或SYN_SENT狀態(tài)),不用進(jìn)程提交數(shù)據(jù),函數(shù)返回。當(dāng)函數(shù)被宏 T C P _ R E A S S 調(diào)用時(shí),返回值 0 被賦給宏的參數(shù)flags。這種做法帶來的副作用是可能會清除 FIN標(biāo)志。當(dāng)宏TCP_REASS被圖29-22的代碼調(diào)用時(shí),如果接收報(bào)文段包含了 SYN、FIN和數(shù)據(jù)(盡管不常見,但卻是有效的報(bào)文段 ),會出現(xiàn)這種情況。1 4 7 - 1 4 9 ti

48、設(shè)定為鏈表的第一個(gè)報(bào)文段。如果鏈表為空,或者第一個(gè)報(bào)文段的起始序號(ti->ti_seq)不等于連接等待接收的下一序號 (rcv_nxt),則函數(shù)返回0。如果第二個(gè)條件報(bào)文段。例如,圖 27-22為真,說明在等待接收的下一序號與已收到的數(shù)據(jù)之間仍然存在中,如果攜帶4 8 字節(jié)的報(bào)文段是鏈表的起始報(bào)文段,但 rcv_nxt等于2,字節(jié)2和3仍舊缺失,因此,不能把4 15字節(jié)提交給應(yīng)用進(jìn)程。返回值 0將清除FIN標(biāo)志(如果該標(biāo)志設(shè)定),這是因?yàn)檫€有未收到的數(shù)據(jù),所以暫時(shí)不能處理 FIN。1 5 0 - 1 5 1 如果連接處于SYN_RCVD狀態(tài),且報(bào)文段長度非零,則函數(shù)返回 0。如果兩個(gè)條

49、件均為真,說明插口在過程中收到了攜帶數(shù)據(jù)的 SYN報(bào)文段。數(shù)據(jù)將保存在連接隊(duì)列中,等待三次握手過程結(jié)束。1 5 2 - 1 6 4循環(huán)從鏈表的第一個(gè)報(bào)文段開始(從前面的測試條件可知,它攜帶數(shù)據(jù)的次序已經(jīng)正確),把數(shù)據(jù)放入插口的接收緩存,并更新 rcv_nxt。當(dāng)鏈表為空,或者鏈表下一報(bào)文段的序號又出現(xiàn)差錯(cuò),即當(dāng)前處理報(bào)文段與下一報(bào)文段間存在報(bào)文段時(shí),循環(huán)結(jié)束。此時(shí),flags變量(函數(shù)的返回值)等于0或者為TH_FIN,取決于放入插口接收緩存的最后一個(gè)報(bào)文段中是否帶有FIN標(biāo)志。在所有mbuf都放入插口的接收緩存后, sorwakeup喚醒所有在插口上等待接收數(shù)據(jù)的應(yīng)用進(jìn)程。27.10tcp_

50、trace函數(shù)圖26-32中,在向IP遞交報(bào)文段之前, tcp_output調(diào)用了tcp_trace函數(shù):if (so->so_options & SO_DEBUG) tcp_trace(TA_OUTPUT, tp->t_state, tp, ti, 0);在內(nèi)核的環(huán)形緩存中添加一條,這些可通過trpt (8)程序。此外,如果內(nèi)核編譯時(shí)定義了符號TCPDEBUG,并且變量tcpconsdebug非零,則信息將輸出到系統(tǒng)任何進(jìn)程都可以設(shè)定TCP的插口選項(xiàng)SO_DEBUG,要求TCP把信息到內(nèi)核的臺。環(huán)形緩存中。但只有進(jìn)程或系統(tǒng)管理員才能運(yùn)行 trpt,因?yàn)樗仨毚娌拍塬@取這些

51、信息。系統(tǒng)內(nèi)盡管可以為任何類型的插口設(shè)定 SO_DUBUG選項(xiàng)(如UDP或原始IP),但只有TCP才會處理它。這些信息被保存在tcp_debug結(jié)構(gòu)中,如圖27-24所示。3 5 - 4 3tcp_debug很大( 196字節(jié)),因?yàn)樗似渌麅蓚€(gè)結(jié)構(gòu):保存 IP和TCP首部的tcpiphdr和完整的TCP塊tcpcb。由于保存了TCP塊,其中的任何變量都可通過trpt打印出來。也就是說,如果 trpt標(biāo)準(zhǔn)輸出中沒有包含讀者感的信息,可修改源代碼以打印塊中任何想要的信息 (Net/3版支持這種修改)。圖25-28中的RTT變量就是通過這種方式得到的。第 27章 TCP的函數(shù)733圖27-24

52、 tcp_debug 結(jié)構(gòu)5 3 - 5 5圖2 7 - 2 4 還定義了數(shù)組 t c p _ d e b u g,也就是前面提到的環(huán)形緩存。數(shù)組指針(tcp_debx)初始化為零,該數(shù)組約占20 000字節(jié)。內(nèi)核只調(diào)用了tcp_trace 4次,每次調(diào)用都會在結(jié)構(gòu)的 td_act變量中存入一個(gè)不同的值,如圖27-25所示。td_act描述參考T A _ D R O P T A _ I N P U TT A _ O U T P U TT A _ U S E R圖29-27 圖29-26 圖26-32 圖30-1當(dāng)輸入報(bào)文段被丟棄時(shí),被tcp_input調(diào)用輸入處理完畢后,調(diào)用tcp_outpu

53、t之前調(diào)用ip_output報(bào)文段之前RPU_ 請求處理完畢后,被tcp_usrreq調(diào)用圖27-25 td_act 值及相應(yīng)的tcp_trace 調(diào)用圖27-26給出了tcp_trace函數(shù)的主要部分,我們忽略了直接輸出到臺的那部分代碼。4 8 - 1 3 3在函數(shù)被調(diào)用時(shí), ostate中保存了連接的前一個(gè)狀態(tài),與連接的當(dāng)前狀態(tài) (保存在塊中)相比較,可了解連接的狀態(tài)變遷狀況。圖 27-25中, TA_OUTPUT不改變連接狀態(tài),但其他3個(gè)調(diào)用則會導(dǎo)致狀態(tài)的轉(zhuǎn)移。圖27-26 tcp_trace 函數(shù):在內(nèi)核的環(huán)形緩存中保存信息734TCP/IP詳解 卷2:實(shí)現(xiàn)圖27-26 (續(xù))輸出舉例圖27-27列出了tcpdump輸出的前4行,反映25.12節(jié)例子中的三次握手過程和

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論