暢談linux下TCP(下)

上一篇介紹了TCP在連接和關閉時候的一些知識點。這篇介紹TCP在連接建立以后,傳輸中的重要特性。

三、TCP傳輸階段

1 、TCP 包頭

先認識一下TCP包頭, 常規(guī)TCP包頭為20個字節(jié)。


圖一.png

可以通過TCP OPTION 擴展包頭內(nèi)容。TCP OPTION 是一個比較靈活的TLV結構,length表示TLV長度,不是僅僅V的長度。如上一篇提到的 TimeStamp

TCP Option - Timestamps: TSval 791379335, TSecr 4104752551
        Kind: Time Stamp Option (8)
        Length: 10
        Timestamp value: 791379335
        Timestamp echo reply: 4104752551

包頭各個字段意思很明顯了,這里就不老調(diào)重彈了。只說一下你能從包頭看出一些有意思的信息嗎:

  • 端口為16個bit,說明最多只能用65535個端口。協(xié)議上已經(jīng)限制死了。即使你修改linux系統(tǒng),擴大端口上限,也是沒有用的。
  • 序列號為32個bit, 說明跑一圈 2^32=4G數(shù)據(jù),就要重復了。 這個問題后面會提到,通過 TCP Option - Timestamps 來配合解決這個限制問題。
  • window size 大小為16個bit。 限制料 傳輸窗口只有64k大小。這個在高速網(wǎng)絡下是個性能缺陷。如何解決呢。還是通過 TCP Option - Window scale 來解決。Window scale 表示要把 window size 放大 多少倍,計算公式為: window_size = window_size * ( 2 ^ Window_scale ) . 看下面這個實例:28960*(2^7) = 144800 字節(jié)


    圖二.png
  • Data offset 為4個bit 最大為15。因為單位是4個字節(jié), 所以tcp 包頭最大長度為 15*4 = 60 個字節(jié)
  • TCP Option 填充體: No-Operation (NOP) 長度為1, 內(nèi)容為 0x01的數(shù)據(jù),可以填充任意個。

2 、seq 和 ack

都說TCP是 有序,可靠的協(xié)議。關鍵是靠seq 和 ack 這兩個字段:

  • seq (Sequence number) : 發(fā)送數(shù)據(jù)包的編號, 用來保證數(shù)據(jù)的順序,數(shù)據(jù)包可能會先發(fā)送的,但是后到達。給每個數(shù)據(jù)包在發(fā)送時按順序編上號碼,接收者按照seq 重新排序。就保證了正確性。
  • ack (Acknowledgment number):期望接收下一個數(shù)據(jù)包的編號。 用來保證包不丟失。發(fā)送者通過這個確認對方到底是否收到包,決定是否丟失,進行重傳.

至于如何利用這兩個字段來保證TCP 有序,可靠的特性。需要用 TCP滑窗來解釋

3、TCP重傳

前面說到過,TCP包頭里面有個window size 字段。用來高速對方,自己的接收能力。 滑窗機制就是利用這兩個字段。來達到這個目錄。

3.1、分包、丟包、亂序

TCP是基于IP協(xié)議的。 用戶發(fā)送一個數(shù)據(jù),對于TCP來說,會存在下列幾種情況:

  • 分包:tcp會對應用層發(fā)送過來的數(shù)據(jù)包進行分包
  • 丟包 :因為網(wǎng)絡故障原因,在路途中丟失了
  • 延遲到達: 因為網(wǎng)絡擁堵,等了很久才到達
  • 亂序:同樣由于網(wǎng)絡擁堵。 先發(fā)的包,比后發(fā)送的包還要后達到接收方.
圖三.png
3.2、重傳機制

包丟了怎么辦,重傳唄,這里涉及到1、什么時機重傳,2、重傳多少問題:

3.2.1、什么時候重傳
  • A、設置一個重傳計時器(RTO): 到達超時時間,就開始重傳。但是難在這超時時間設置多大。內(nèi)網(wǎng)可能 10ms 就可以算超時,外網(wǎng)可能100ms才算超時。如果設置成 50ms超時,對內(nèi)網(wǎng)來說太慢,降低傳輸速度。對外網(wǎng)來說太短,可能包只是延遲而已,剛重傳玩就到了,造成重復傳輸,對本來已經(jīng)擁堵的網(wǎng)絡造成更大負擔,積累下去就是惡性循環(huán)。
    那如何解決定時器時長的問題呢? 就是超時時間不是靜態(tài)不變的,根據(jù)網(wǎng)絡狀況動態(tài)調(diào)整。是一套比較復雜的公式(對公式感興趣的自行去了解)?;赗TT(Round Trip Time) 計算 RTO((Retransmission TimeOut)), 使用RTO作為超時時間,這個值是動態(tài)變化的。 RTT是收到ACK的應答時間戳 減 這個包的發(fā)送時間戳。

關于RTT計算問題:
但是假如這個包重傳了一次, 收到ACK。 這個ACK到底是前一個包的延遲到達呢,還是后一個重發(fā)包的應答呢,無法分辨這個ACK是誰的? 如下圖,有多種計算方法, 如果隨意取其中一個,會帶來比你想象要大很多的偏差結果。

圖四.png

如果系統(tǒng)支持Timestamps的話, 可以通過上一章中提到的 TCP Option Timestamps。 因為每個ACK都帶有發(fā)送者的timestamp。這樣就很明確計算RTT了。但是不是每個系統(tǒng)都支持這種 Timestamps

TCP Option - Timestamps: TSval 791379335, TSecr 4104752551
    Kind: Time Stamp Option (8)
    Length: 10
    Timestamp value: 791379335
    Timestamp echo reply: 4104752551

注解:TCP Option Timestamps 是 表示系統(tǒng)啟動以來時間。單位為毫秒。 (2^32/1000/3600/24 = 49 天一個輪回周期)

如果不支持的話,就取最早的那次傳輸時間計算RTT(不按重傳計算).
最后linux是通過一個公式計算RTO,使用這個作為重傳超時時間。這個公式能消除RTT的取值誤差影響,至于為什么能做到,我只知道實際TCP驗證如此,經(jīng)過檢驗出來的。

  • B、快速重傳機制(Fast Retransmit): 快重傳要求 接收方在收到報文段后就立即發(fā)出確認,例如 圖五 中,收到#5報文,立即發(fā)送(后面會提到delay-ack,ack有時候會隨著后續(xù)數(shù)據(jù)報文一起捎帶出去,注意這里是不延遲,立即發(fā)送) ack=3, 為的是使發(fā)送方及早知道#3報文沒有到達。發(fā)送方只要一連收到三個重復確認就應當立即重傳對方尚未收到的報文段(下圖接收方收到#5, #7,#9號個報文,都是重復回應ack=3),這時候,發(fā)送方就知道#3報文丟失,開始重傳#3報文,而不必繼續(xù)等待設置的重傳計時器時間到期才開始重傳。 (這種算法,據(jù)說可提高網(wǎng)絡吞吐量約20%)
    圖五.png
3.2.2 重傳次數(shù)問題

重傳不是只有一次,會有多次。通過 tcp_retries1, tcp_retries2 這兩個參數(shù)來控制。默認情況下 tcp_retries1 =3,tcp_retries2 =15。

net.ipv4.tcp_retries1 
net.ipv4.tcp_retries2 

超過tcp_retries1 后 會更新路由,選擇一條新的路由,避免路由問題導致丟包或者延遲。另外也不完全是由這兩個參數(shù)控制。還有一個總的超時時間值,根據(jù)初始RTO計算出來。如果這個值比較小,可能不到重試 tcp_retries2 次數(shù)就結束了。總體來說取兩者最小。如果最終還是收不到應答。就會直接放棄重傳,關閉TCP連接。

3.2.3、重傳多少的問題

如圖五所示, 發(fā)送方在發(fā)送完#9號包后,這時候已經(jīng)收到連續(xù)3個ack=3的ACK報文,在快速重傳算法下。開始重傳。此時只是重傳#3報文,還是重傳{ #3,#4,#5,#6,#7,#8,#9} 。 只重傳#3報文,效率有點低,后面又要同樣等待重傳#6號報文。重傳后面所有報文。會導致已經(jīng)收到的報文又重發(fā),造成網(wǎng)絡交通更加擁堵。 最好的發(fā)式是 只重傳 丟失的報文 {#3,#4,#6,#8}. 但是發(fā)送方無法知道這么多信息。于是乎。SACK方案被提出來,解決這個問題.

  • Selective Acknowledgment (SACK): 同樣通過Tcp Option 擴展。在TCP頭里加一個SACK的東西,ACK攜帶的ack num 還是的 快速重傳得 ack num。但是還會帶上 已經(jīng)收到的 報文段落。
        TCP Option - SACK permitted
            Kind: SACK Permitted (4)
            Length: 2
圖六.png

這里可以看到,通過SACK選項,解決了按需重傳的要求。發(fā)送方通過SACK,知道該重傳那些報文,避免重復重傳。

在三次握手的時候,會在雙方的syn包中攜帶本地是否啟動sack擴展。 Sack-Permitted選項就是說明本地啟用sack

由于TCP包頭長度有限(60),所以SACK的片段個數(shù)也是有限的,最多4個. 如果有TCP Option其他選項,會小于4個。大家可以自己計算.

TCP SACK Option:
Kind: 5
Length: Variable
                  +--------+--------+
                  | Kind=5 | Length |
+--------+--------+--------+--------+
|      Left Edge of 1st Block       |
+--------+--------+--------+--------+
|      Right Edge of 1st Block      |
+--------+--------+--------+--------+
|                                   |
/            . . .                  /
|                                   |
+--------+--------+--------+--------+
|      Left Edge of nth Block       |
+--------+--------+--------+--------+
|      Right Edge of nth Block      |
+--------+--------+--------+--------+

  • DSACK(Duplicate SACK): 因為網(wǎng)絡延遲原因,有些延遲報文會被當成丟失報文,讓接收方通過SACK告訴發(fā)送方。還是會導致有些報文重復接收問題。于是發(fā)送方通過SACK 可以判斷出哪些報文重復傳輸了,進而調(diào)整自己的RTO或擁塞控制。
    DSACK沒有單獨額外去定義TCP Option. 而是共用了SACK的 TCP Option。 他使用 了 SACK的一個段位置。那么接收方如何判斷 這個第一個段是 SACK還是DSACK的呢。 按照下面邏輯去判斷
 如果SACK的第一個段的范圍被ACK所覆蓋,那么就是DSACK
 如果SACK的第一個段的范圍被SACK的第二個段覆蓋,那么就是DSACK

4、流量控制

4.1 滑動窗口(Sliding Window)

發(fā)送端和接收端都有緩沖區(qū),但是緩沖區(qū)大小是由限的。假如接收端比較繁忙,沒有來得及取走緩沖區(qū)的數(shù)據(jù),發(fā)送端如果還一直發(fā)送,就會有問題了。需要有種機制,接收方通知發(fā)送方自己接收能力,讓發(fā)送方調(diào)整發(fā)送速度。

TCP頭里有一個字段叫Window Size,又叫Advertised-Window,這個字段是接收端告訴發(fā)送端自己還有多少緩沖區(qū)可以接收數(shù)據(jù)。于是發(fā)送端就可以根據(jù)這個接收端的處理能力來發(fā)送數(shù)據(jù),而不會導致接收端處理不過來。

發(fā)送方有個發(fā)送緩沖區(qū),發(fā)送緩沖區(qū)的布局如下:

圖七.png

圖七中各段含義

  • Category#1已收到ack確認的數(shù)據(jù)。
  • Category#2發(fā)還沒收到ack的。
  • Category#3在窗口中還沒有發(fā)出的(接收方還有空間)。
  • Category#4窗口以外的數(shù)據(jù)(接收方?jīng)]空間)

Category#2 + Category#3就是發(fā)送者的TCP滑動窗口

4.2 滑窗是動態(tài)伸縮&前進

  • 這個窗口大小時動態(tài)變化的。當接收方在TCP包頭里面通知增大windows-size時候(rwnd), 這個窗口就是增加,當通知減小windows-size時候,這個窗口就減小。所以這個窗口的大小很大程度實際是由接收端控制(實際上是 Min [rwnd, cwnd], 至于cwnd的定義,后面擁塞控制會提到)。
  • 這個窗口是不斷向前移動的,隨著ack的確認,不停的向前移動。
圖八.png

圖八生動的演示了滑窗的移動和大小伸縮的變化。

4.3、流量控制

如果發(fā)送者發(fā)送數(shù)據(jù)過快,接收者來不及接收,那么就會有分組丟失。為了避免分組丟失,控制發(fā)送者的發(fā)送速度,使得接收者來得及接收,這就是流量控制。流量控制根本目的是防止分組丟失,它是構成TCP可靠性的一方面。

如何實現(xiàn)流量控制?由滑動窗口的存在。既保證了分組無差錯、有序接收,也實現(xiàn)了流量控制。主要的方式就是接收方返回的 ACK 中會包含自己的接收窗口的大小,并且利用大小來控制發(fā)送方的數(shù)據(jù)發(fā)送。

5、擁塞控制

擁塞控制是防止傳輸數(shù)據(jù)的聯(lián)絡層網(wǎng)絡出擁塞時數(shù)據(jù)大量丟失的情況。和流量控制 不同的是;流量控制主要是參考接收方的能力指標(rwnd),進行發(fā)送速度調(diào)整。 擁塞控制主要是參考網(wǎng)絡延遲來調(diào)整發(fā)送速度。因為光依賴接收方和發(fā)送方的信息參考,并不完整。需要考慮傳輸網(wǎng)絡狀況。于是誕生了幾個參考網(wǎng)絡狀態(tài)調(diào)整發(fā)送速度的算法:

擁塞控制主要算法有: 慢啟動、擁塞避免、快重傳、快恢復。其中快速重傳 前面已經(jīng)介紹過。

5.1、慢啟動
5.1.1、MTU(Maximum Transmission Unit)

L2 層的限制。 MTU(Maximum Transmission Unit)最大傳輸單元, 這個是由以太網(wǎng)鏈路層決定的長度,提供給其上層最大一次傳輸數(shù)據(jù)的大小。如果上層是 IP 協(xié)議的話, 缺省MTU=1500。意思是 一個 鏈路層 IP報文的Payload (包含ip 頭,tcp頭) 不能超過1500個字節(jié)。對于tcp應用層來說 max = 1500 - 20 -20 = 1460 , max = 1500-20-60= 1420 。

圖九.png

TCP應用層一般會發(fā)送幾K或幾M字節(jié)。鏈路層會按照MTU大小切分一段段發(fā)送出去。

PS: 鏈路層協(xié)議有很多,不同的鏈路層協(xié)議,其MTU大小也不一樣。 1500只是以太網(wǎng)絡下的最大MTU大小。

而且,這里計算TCP包在 [1420-1460] 之間,其實是沒有考慮到第三層IP協(xié)議包頭的擴展。其實IP包頭也不是20個字節(jié)固定,也是可以擴展。

5.1.2、MSS(Maximum Segment Size )

L4層的限制。 MSS(Maximum Segment Size ) 最大TCP分段大小,不包含TCP頭和 option,只包含TCP Payload ,TCP用來限制自己每次發(fā)送的最大分段尺寸. 應用層發(fā)送數(shù)據(jù),都要按照這個大小切片,發(fā)出去。

MSS 缺省是1460字節(jié),不過這個在TCP握手階段協(xié)商的。也是通過TCP Option來協(xié)商. 發(fā)送方和接收方不一定要一樣。


圖十.png
發(fā)送方:
TCP Option - Maximum segment size: 1460 bytes
Kind: Maximum Segment Size (2)
Length: 4
MSS Value: 1460
接收方:
TCP Option - Maximum segment size: 1300 bytes
    Kind: Maximum Segment Size (2)
    Length: 4
    MSS Value: 1300

可用通過 setsockopt 借口設置 TCP_MAXSEG 選項來設置MSS

PS: 從MTP和MSS定義可以看出他們關系 MSS 永遠要小于MTU。 目的是盡量避免在IP層再次分片。

5.1.3、擁塞窗口cwnd(congestion window)

擁塞窗口是發(fā)送方維持的發(fā)送窗口大小,, 注意前面有個rwnd,表示接收方的能力。 真正的發(fā)送者發(fā)送窗口=min(rwnd, cwnd)。cwnd 的 計量單位是 MSS。 即 cwnd=1 表示 一個 MSS大小。 cwnd=2 表示2個 MSS大小。
cwnd大小也是根據(jù)網(wǎng)絡狀態(tài),動態(tài)變化的。

5.1.4、慢啟動算法

慢啟動算法的思路就是: 不要一開始就發(fā)送大量的數(shù)據(jù),這樣很容易導致網(wǎng)絡中路由器緩存空間耗盡,從而發(fā)生擁塞,而是先由小到大逐漸增加發(fā)送窗口的大小,探測一下網(wǎng)絡的擁塞程度。

那么cwnd如何增長呢? 增長到什么程度停下來呢? 這里有個關鍵指標 慢啟動門限ssthresh。
慢啟動的算法如下: (cwnd全稱Congestion Window):

1、連接建好的開始先初始化cwnd = 1,表明可以傳一個MSS大小的數(shù)據(jù)。

2、每當收到一個ACK,cwnd++; 呈線性上升

3、每當過了一個RTT,cwnd = cwnd*2; 呈指數(shù)讓升

4、還有一個ssthresh(slow start threshold),是一個上限,當cwnd >= ssthresh時,就會進入“擁塞避免算法”(后面會說這個算法)

圖11.png

可以看出,慢啟動其實并不慢,基本上是以指數(shù)級增長的

PS:如果出現(xiàn)超時,則執(zhí)行下面擁塞避免算法。 cwnd=1重新開始。

5.2、擁塞避免 (Congestion Avoidance)

擁塞避免算法可以簡單歸納成一句話:“加法增加, 乘法減少”。
前面說過,有一個ssthresh(slow start threshold),是cwnd的上限,當cwnd >= ssthresh時,就會進入“擁塞避免算法”。一般來說ssthresh的值是64k(65535,單位是字節(jié)),當cwnd達到這個值時后,算法如下:

1、收到一個ACK時,cwnd = cwnd + 1/cwnd

2、當每過一個RTT時,cwnd = cwnd + 1

這樣就可以避免增長過快導致網(wǎng)絡擁塞,慢慢的增加調(diào)整到網(wǎng)絡的最佳值?;旧鲜且?strong>線性增長的。

當出現(xiàn)重傳的時候,算法又會分成l有兩種情況:
1)等到RTO超時,重傳數(shù)據(jù)包。TCP認為這種情況太糟糕,反應也很強烈。

1、sshthresh = cwnd /2 (乘法減少

2、cwnd 重置為 1

3、進入慢啟動過程

2)遇到快速重傳情況,也就是在收到3個duplicate ACK時就開啟重傳,使用 快速恢復算法——Fast Recovery

可以看出,整個擁塞控制過錯可以用 乘法減?。∕ultiplicative Decrease)和加法增大(Additive Increase)來概括, 簡稱MDAI。

圖12.png
5.3、快速恢復算法(Fast Recovery)

快速恢復算法依賴前面講的快速重傳算法。即當出現(xiàn)連續(xù)三個重復ACK時候。不啟用擁塞避免算法,而是

1、sshthresh = sshthresh /2 (乘法減少

2、cwnd = sshthresh

3、進入加法增加, 乘法減少擁塞避免算法

圖13.png

比較前面擁塞避免算法, 可以看出最大的區(qū)別是沒有一下子把cwnd降到1,而是降到一般開始重新嘗試。因此 快速恢復算法相對來說比較樂觀激進一些。它的主要思路是“認為如果連續(xù)收到3個重復的ACK,網(wǎng)絡擁塞狀況沒有想象那么糟糕,可以嘗試適當減緩一下發(fā)送速度”

5、TCP其它相關算法

5.1、Nagle算法(納格算法)

在實際發(fā)送數(shù)據(jù)中,可能會存在一種情況。發(fā)送方幾個字節(jié)幾個字節(jié)的發(fā)送數(shù)據(jù)給對端。這樣就會造成資源極大的浪費。網(wǎng)絡中傳輸?shù)臄?shù)據(jù),報文大部分是協(xié)議的包頭(IP+TCP 包頭40個字節(jié))。另外還會引起大量ACK恢復。對網(wǎng)絡擁塞危害很大。為了避免這種情況。出現(xiàn)了 Nagle算法.

Nagle算法避免發(fā)送大量的小包,防止小包泛濫于網(wǎng)絡。把多次發(fā)送的小包合并成一次發(fā)送。

(1)如果包長度達到MSS,則允許發(fā)送;

(2)如果該包含有FIN,則允許發(fā)送;

(3)設置了TCP_NODELAY選項,則允許發(fā)送;

(4)未設置TCP_CORK選項時,若所有發(fā)出去的小數(shù)據(jù)包(包長度小于MSS)均被確認,則允許發(fā)送;

(5)上述條件都未滿足,但發(fā)生了超時(一般為200ms),則立即發(fā)送。

問題:
1、從上面第4個條件可以看到,如果能快速收到 接收方回復ACK,其實Nagle算法基本上失效了;
2、因為Nagle算法 會合并包。所以就會導致TCP常見 "粘包",“并包” 現(xiàn)象。在某些場景可能會出問題。后面會在TCP_NODELAY 中提到;

Nagle算法 在LINUX系統(tǒng)中,缺省時開啟的。如果要禁止的話,使用TCP_NODELAY 選項關閉

看個例子,下面例子中, 使用nagle算法 可能會節(jié)省時間。


圖14.png

因為在linux系統(tǒng)中,agle算法 缺省是開啟的,說明實際網(wǎng)絡中,前一種情況很常見。大部分能節(jié)省時間。

5.2、Delayed ACK

Nagle算法 是針對發(fā)送方的,進行小報文合并。 Delayed ACK是 另外一個角度。針對接收方,對連續(xù)ACK進行合并。

(1)如果有數(shù)據(jù)回復對方,會捎帶上ACK;

(2)如果有有2個連續(xù)ACK未回復,則合并,則發(fā)送ACK;

(3)Delayed ACK 有超時定時器(缺省是200ms),超時則發(fā)送ACK;

Delayed ACK 缺省時打開的,如果想要關閉,使用TCP_QUICKACK

int off = 0;
setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, &off, sizeof(off));

有時候,開啟 Delayed ACK,反而會增加傳輸時間,如圖15所示


圖15.png

如果配合Nagle 算法,則有可能導致更長的時間,如圖16所示

圖16.png

5.3、TCP_CORK

首先,TCP_CORK則是Linux系統(tǒng)所獨有的。其它*nix系統(tǒng)是沒有這個選項。

cork就是塞子的意思,TCP_CORK 是禁止小報文發(fā)送,合并成一個大報文(>=MSS)發(fā)送出去。初看起來,TCP_CORK 的 描述和用途經(jīng)常會和Nagle算法搞混淆,兩者都是會合并小報文為一個大報文,一次發(fā)出去。但是兩者又有不同。

(1)如果待發(fā)送數(shù)據(jù)包大小超過MSS, 則發(fā)送出去;

(2)如果不足MSS,則會在超時時間內(nèi)(200 ms)發(fā)送出去;

TCP_CORK其實是更新激進的Nagle算法,完全禁止小包發(fā)送,而Nagle算法沒有禁止小包發(fā)送,只是禁止了大量的小包發(fā)送。 在很多時候,我們鼓勵禁止Nagle,因為ack回的快的話,相當于Nagle失效,ACK回的慢的話,Nagle要等待所有的ACK都應答后才傳輸,往往浪費掉很多時間。 而是使用TCP_CORK 代替, TCP_CORK 效率可能更高。

int state = 1;
setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state));

5.4、TCP_NODELAY

-TCP_NODELAY 表示禁止延遲發(fā)送,也就是會禁止 Nagle 算法 。
-TCP_CORK 和 TCP_NODELAY Linux 2.5.71 之前版本不能一起使用。之后一起使用的話,TCP_CORK會覆蓋TCP_NODELAY 。

5.5、TCP_QUICKACK

-TCP_QUICKACK 是 和 Delayed ACK 互斥的。

6、結語

TCP博大精深,非常復雜,我這里只是管中窺豹。最后獻上鎮(zhèn)樓圖一張 TCP 狀態(tài)圖TCP Finite State Machine (FSM)

圖17.png
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容