自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

直擊案發(fā)現(xiàn)場!TCP 10倍延遲的真相是?

企業(yè)動態(tài)
什么是經(jīng)驗?就是遇到問題,解決問題,總結(jié)方法。遇到的問題多了,解決的辦法多了,經(jīng)驗自然就積累出來了。

什么是經(jīng)驗?就是遇到問題,解決問題,總結(jié)方法。遇到的問題多了,解決的辦法多了,經(jīng)驗自然就積累出來了。今天的文章是阿里技術(shù)專家蟄劍在工作中遇到的一個問題引發(fā)的對TCP性能和發(fā)送接收Buffer關(guān)系的系列思考(問題:應(yīng)用通過專線從公司訪問阿里云上的服務(wù),專線100M,時延20ms,一個SQL查詢了22M數(shù)據(jù)出現(xiàn)10倍+的信息延遲,不正常。)希望,你也能從中得到啟發(fā)。

前言

本文希望解析清楚,當(dāng)我們在代碼中寫下 socket.setSendBufferSize 和 sysctl 看到的rmem/wmem系統(tǒng)參數(shù)以及最終我們在TCP常常談到的接收發(fā)送窗口的關(guān)系,以及他們怎樣影響TCP傳輸?shù)男阅堋?/p>

先明確一下:文章標(biāo)題中所說的Buffer指的是sysctl中的 rmem或者wmem,如果是代碼中指定的話對應(yīng)著SO_SNDBUF或者SO_RCVBUF,從TCP的概念來看對應(yīng)著發(fā)送窗口或者接收窗口。

TCP性能和發(fā)送接收Buffer的關(guān)系

相關(guān)參數(shù):

  1. $sudo sysctl -a | egrep "rmem|wmem|adv_win|moderate" 
  2. net.core.rmem_default = 212992 
  3. net.core.rmem_max = 212992 
  4. net.core.wmem_default = 212992 
  5. net.core.wmem_max = 212992 
  6. net.ipv4.tcp_adv_win_scale = 1 
  7. net.ipv4.tcp_moderate_rcvbuf = 1 
  8. net.ipv4.tcp_rmem = 4096    87380   6291456 
  9. net.ipv4.tcp_wmem = 4096    16384   4194304 
  10. net.ipv4.udp_rmem_min = 4096 
  11. net.ipv4.udp_wmem_min = 4096 
  12. vm.lowmem_reserve_ratio = 256   256 32 

先從碰到的一個問題看起:

應(yīng)用通過專線從公司訪問阿里云上的服務(wù),專線100M,時延20ms,一個SQL查詢了22M數(shù)據(jù),結(jié)果花了大概25秒,這太慢了,不正常。如果通過云上client訪問云上服務(wù)那么1-2秒就返回了(說明不跨網(wǎng)絡(luò)服務(wù)是正常的)。如果通過http或者scp從公司向云上傳輸這22M的數(shù)據(jù)大概兩秒鐘也傳送完畢了(說明網(wǎng)絡(luò)帶寬不是瓶頸),所以這里問題的原因基本上是我們的服務(wù)在這種網(wǎng)絡(luò)條件下有性能問題,需要找出為什么。 

抓包 tcpdump+wireshark

這個查詢結(jié)果22M的需要25秒,如下圖(wireshark 時序圖),橫軸是時間,縱軸是sequence number:

粗一看沒啥問題,因為時間太長掩蓋了問題。把這個圖形放大,就看中間50ms內(nèi)的傳輸情況(橫軸是時間,縱軸是sequence number,一個點代表一個包)。 


換個角度,看看窗口尺寸圖形:

從bytes in flight也大致能算出來總的傳輸時間 16K*1000/20=800Kb/秒我們的應(yīng)用會默認(rèn)設(shè)置 socketSendBuffer 為16K:

socket.setSendBufferSize(16*1024) //16K send buffer

來看一下tcp包發(fā)送流程:

圖片來源:陶輝

如果sendbuffer不夠就會卡在上圖中的第一步 sk_stream_wait_memory,通過systemtap腳本可以驗證:

  1. #!/usr/bin/stap 
  2. # Simple probe to detect when a process is waiting for more socket send 
  3. # buffer memory. Usually means the process is doing writes larger than the 
  4. # socket send buffer size or there is a slow receiver at the other side. 
  5. # Increasing the socket's send buffer size might help decrease application 
  6. # latencies, but it might also make it worse, so buyer beware. 
  7.  
  8. # Typical outputtimestamp in microseconds: procname(pid) event 
  9. # 1218230114875167: python(17631) blocked on full send buffer 
  10. # 1218230114876196: python(17631) recovered from full send buffer 
  11. # 1218230114876271: python(17631) blocked on full send buffer 
  12. # 1218230114876479: python(17631) recovered from full send buffer 
  13. probe kernel.function("sk_stream_wait_memory"
  14.     printf("%u: %s(%d) blocked on full send buffern"
  15.         gettimeofday_us(), execname(), pid()) 
  16.  
  17. probe kernel.function("sk_stream_wait_memory").return 
  18.     printf("%u: %s(%d) recovered from full send buffern"
  19.         gettimeofday_us(), execname(), pid()) 

原理解析

如果tcp發(fā)送buffer也就是SO_SNDBUF只有16K的話,這些包很快都發(fā)出去了,但是這16K不能立即釋放出來填新的內(nèi)容進(jìn)去,因為tcp要保證可靠,萬一中間丟包了呢。只有等到這16K中的某些包ack了,才會填充一些新包進(jìn)來然后繼續(xù)發(fā)出去。由于這里rt基本是20ms,也就是16K發(fā)送完畢后,等了20ms才收到一些ack,這20ms應(yīng)用、內(nèi)核什么都不能做,所以就是如第二個圖中的大概20ms的等待平臺。

sendbuffer相當(dāng)于發(fā)送倉庫的大小,倉庫的貨物都發(fā)走后,不能立即騰出來發(fā)新的貨物,而是要等對方確認(rèn)收到了(ack)才能騰出來發(fā)新的貨物。 傳輸速度取決于發(fā)送倉庫(sendbuffer)、接收倉庫(recvbuffer)、路寬(帶寬)的大小,如果發(fā)送倉庫(sendbuffer)足夠大了之后接下來的瓶頸就是高速公路了(帶寬、擁塞窗口)。

如果是UDP,就沒有可靠的概念,有數(shù)據(jù)統(tǒng)統(tǒng)發(fā)出去,根本不關(guān)心對方是否收到,也就不需要ack和這個發(fā)送buffer了。

幾個發(fā)送buffer相關(guān)的內(nèi)核參數(shù):

  1. vm.lowmem_reserve_ratio = 256   256     32 
  2. net.core.wmem_max = 1048576 
  3. net.core.wmem_default = 124928 
  4. net.ipv4.tcp_wmem = 4096        16384   4194304 
  5. net.ipv4.udp_wmem_min = 4096 

net.ipv4.tcp_wmem 默認(rèn)就是16K,而且是能夠動態(tài)調(diào)整的,只不過我們代碼中這塊的參數(shù)是很多年前從Cobra中繼承過來的,初始指定了sendbuffer的大小。代碼中設(shè)置了這個參數(shù)后就關(guān)閉了內(nèi)核的動態(tài)調(diào)整功能,但是能看到http或者scp都很快,因為他們的send buffer是動態(tài)調(diào)整的,所以很快。

接收buffer是有開關(guān)可以動態(tài)控制的,發(fā)送buffer沒有開關(guān)默認(rèn)就是開啟,關(guān)閉只能在代碼層面來控制:

  1. net.ipv4.tcp_moderate_rcvbuf 

優(yōu)化

調(diào)整 socketSendBuffer 到256K,查詢時間從25秒下降到了4秒多,但是比理論帶寬所需要的時間略高。

繼續(xù)查看系統(tǒng) net.core.wmem_max 參數(shù)默認(rèn)最大是130K,所以即使我們代碼中設(shè)置256K實際使用的也是130K,調(diào)大這個系統(tǒng)參數(shù)后整個網(wǎng)絡(luò)傳輸時間大概2秒(跟100M帶寬匹配了,scp傳輸22M數(shù)據(jù)也要2秒),整體查詢時間2.8秒。測試用的mysql client短連接,如果代碼中的是長連接的話會塊300-400ms(消掉了慢啟動階段),這基本上是理論上最快速度了。

如果指定了tcp_wmem,則net.core.wmem_default被tcp_wmem的覆蓋。send Buffer在tcp_wmem的最小值和最大值之間自動調(diào)整。如果調(diào)用setsockopt()設(shè)置了socket選項SO_SNDBUF,將關(guān)閉發(fā)送端緩沖的自動調(diào)節(jié)機(jī)制,tcp_wmem將被忽略,SO_SNDBUF的最大值由net.core.wmem_max限制。

BDP 帶寬時延積

BDP=rtt*(帶寬/8)

這個 buffer 調(diào)到1M測試沒有幫助,從理論計算BDP(帶寬時延積) 0.02秒*(100MB/8)=250Kb 所以SO_SNDBUF為256Kb的時候基本能跑滿帶寬了,再大實際意義也不大了。也就是前面所說的倉庫足夠后瓶頸在帶寬上了。

因為BDP是250K,也就是擁塞窗口(帶寬、接收窗口和rt決定的)即將成為新的瓶頸,所以調(diào)大buffer沒意義了。

用tc構(gòu)造延時和帶寬限制的模擬重現(xiàn)環(huán)境

  1. sudo tc qdisc del dev eth0 root netem delay 20ms 
  2. sudo tc qdisc add dev eth0 root tbf rate 500kbit latency 50ms burst 15kb 

這個案例關(guān)于wmem的結(jié)論

默認(rèn)情況下Linux系統(tǒng)會自動調(diào)整這個buffer(net.ipv4.tcp_wmem), 也就是不推薦程序中主動去設(shè)置SO_SNDBUF,除非明確知道設(shè)置的值是最優(yōu)的。

從這里我們可以看到,有些理論知識點雖然我們知道,但是在實踐中很難聯(lián)系起來,也就是常說的無法學(xué)以致用,最開始看到抓包結(jié)果的時候比較懷疑發(fā)送、接收窗口之類的,沒有直接想到send buffer上,理論跟實踐的鴻溝。

說完發(fā)送Buffer(wmem)接下來我們接著一看看接收buffer(rmem)和接收窗口的情況

用這樣一個案例下來驗證接收窗口的作用:

有一個batch insert語句,整個一次要插入5532條記錄,所有記錄大小總共是376K。SO_RCVBUF很小的時候并且rtt很大對性能的影響

如果rtt是40ms,總共需要5-6秒鐘:

基本可以看到server一旦空出來點窗口,client馬上就發(fā)送數(shù)據(jù),由于這點窗口太小,rtt是40ms,也就是一個rtt才能傳3456字節(jié)的數(shù)據(jù),整個帶寬才80-90K,完全沒跑滿。

 

比較明顯間隔 40ms 一個等待臺階,臺階之間兩個包大概3K數(shù)據(jù),總的傳輸效率如下:

 

斜線越陡表示速度越快,從上圖看整體SQL上傳花了5.5秒,執(zhí)行0.5秒。

此時對應(yīng)的窗口尺寸:

 

窗口由最開始28K(20個1448)很快降到了不到4K的樣子,然后基本游走在即將滿的邊緣,雖然讀取慢,幸好rtt也大,導(dǎo)致最終也沒有滿。(這個是3.1的Linux,應(yīng)用SO_RCVBUF設(shè)置的是8K,用一半來做接收窗口)。

SO_RCVBUF很小的時候并且rtt很小時對性能的影響

如果同樣的語句在 rtt 是0.1ms的話:

雖然明顯看到接收窗口經(jīng)常跑滿,但是因為rtt很小,一旦窗口空出來很快就通知到對方了,所以整個過小的接收窗口也沒怎么影響到整體性能。

 

如上圖11.4秒整個SQL開始,到11.41秒SQL上傳完畢,11.89秒執(zhí)行完畢(執(zhí)行花了0.5秒),上傳只花了0.01秒,接收窗口情況:

 

如圖,接收窗口由最開始的28K降下來,然后一直在5880和滿了之間跳動:

 

從這里可以得出結(jié)論,接收窗口的大小對性能的影響,rtt越大影響越明顯,當(dāng)然這里還需要應(yīng)用程序配合,如果應(yīng)用程序一直不讀走數(shù)據(jù)即使接收窗口再大也會堆滿的。

SO_RCVBUF和tcp window full的壞case

 

上圖中紅色平臺部分,停頓了大概6秒鐘沒有發(fā)任何有內(nèi)容的數(shù)據(jù)包,這6秒鐘具體在做什么如下圖所示,可以看到這個時候接收方的TCP Window Full,同時也能看到接收方(3306端口)的TCP Window Size是8192(8K),發(fā)送方(27545端口)是20480。

 

這個狀況跟前面描述的recv buffer太小不一樣,8K是很小,但是因為rtt也很小,所以server總是能很快就ack收到了,接收窗口也一直不容易達(dá)到full狀態(tài),但是一旦接收窗口達(dá)到了full狀態(tài),居然需要驚人的6秒鐘才能恢復(fù),這等待的時間有點太長了。這里應(yīng)該是應(yīng)用讀取數(shù)據(jù)太慢導(dǎo)致了耗時6秒才恢復(fù),所以最終這個請求執(zhí)行會非常非常慢(時間主要耗在了上傳SQL而不是執(zhí)行SQL)。

實際原因不知道,從讀取TCP數(shù)據(jù)的邏輯來看這里沒有明顯的block,可能的原因:

  • request的SQL太大,Server(3306端口上的服務(wù))從TCP讀取SQL需要放到一塊分配好的內(nèi)存,內(nèi)存不夠的時候需要擴(kuò)容,擴(kuò)容有可能觸發(fā)fgc,從圖形來看,第一次滿就卡頓了,而且每次滿都卡頓,不像是這個原因
  • request請求一次發(fā)過來的是多個SQL,應(yīng)用讀取SQL后,將SQL分成多個,然后先執(zhí)行第一個,第一個執(zhí)行完后返回response,再讀取第二個。圖形中卡頓前沒有response返回,所以也不是這個原因。
  • ……其它未知原因

接收方不讀取數(shù)據(jù)導(dǎo)致的接收窗口滿同時有丟包發(fā)生

服務(wù)端返回數(shù)據(jù)到client端,TCP協(xié)議棧ack這些包,但是應(yīng)用層沒讀走包,這個時候 SO_RCVBUF 堆積滿,client的TCP協(xié)議棧發(fā)送 ZeroWindow 標(biāo)志給服務(wù)端。也就是接收端的 buffer 堆滿了(但是服務(wù)端這個時候看到的bytes in fly是0,因為都ack了),這時服務(wù)端不能繼續(xù)發(fā)數(shù)據(jù),要等 ZeroWindow 恢復(fù)。

那么接收端上層應(yīng)用不讀走包可能的原因:

  • 應(yīng)用代碼卡頓、GC等等

應(yīng)用代碼邏輯上在做其它事情(比如Server將SQL分片到多個DB上,Server先讀取第一個分片,如果第一個分片數(shù)據(jù)很大很大,處理也慢,那么第二個分片數(shù)據(jù)都返回到了TCP buffer,也沒去讀取其它分片的結(jié)果集,直到第一個分片讀取完畢。如果SQL帶排序,那么Server。

  • 會輪詢讀取多個分片,造成這種卡頓的概率小了很多

上圖這個流因為應(yīng)用層不讀取TCP數(shù)據(jù),導(dǎo)致TCP接收Buffer滿,進(jìn)而接收窗口為0,server端不能再發(fā)送數(shù)據(jù)而卡住,但是ZeroWindow的探測包,client都有正?;貜?fù),所以1903秒之后接收方窗口不為0后(window update)傳輸恢復(fù)。

這個截圖和前一個類似,是在Server上(3003端口)抓到的包,不同的是接收窗口為0后,server端多次探測(Server上抓包能看到),但是client端沒有回復(fù) ZeroWindow(也有可能是回復(fù)了,但是中間環(huán)節(jié)把a(bǔ)ck包丟了,或者這個探測包client沒收到),造成server端認(rèn)為client死了、不可達(dá)之類,進(jìn)而反復(fù)重傳,重傳超過15次之后,server端認(rèn)為這個連接死了,粗暴單方面斷開(沒有reset和fin,因為沒必要,server認(rèn)為網(wǎng)絡(luò)連通性出了問題)。

等到1800秒后,client的接收窗口恢復(fù)了,發(fā)個window update給server,這個時候server認(rèn)為這個連接已經(jīng)斷開了,只能回復(fù)reset。

網(wǎng)絡(luò)不通,重傳超過一定的時間(tcp_retries2)然后斷開這個連接是正常的,這里的問題是:

為什么這種場景下丟包了,而且是針對某個stream一直丟包?

可能是因為這種場景下觸發(fā)了中間環(huán)節(jié)的流量管控,故意丟包了(比如proxy、slb、交換機(jī)都有可能做這種選擇性的丟包)。

這里server認(rèn)為連接斷開,沒有發(fā)reset和fin,因為沒必要,server認(rèn)為網(wǎng)絡(luò)連通性出了問題。client還不知道server上這個連接清理掉了,等client回復(fù)了一個window update,server早就認(rèn)為這個連接早斷了,突然收到一個update,莫名其妙,只能reset。

接收窗口和SO_RCVBUF的關(guān)系

初始接收窗口一般是 mss乘以初始cwnd(為了和慢啟動邏輯兼容,不想一下子沖擊到網(wǎng)絡(luò)),如果沒有設(shè)置SO_RCVBUF,那么會根據(jù) net.ipv4.tcp_rmem 動態(tài)變化,如果設(shè)置了SO_RCVBUF,那么接收窗口要向下面描述的值靠攏。

初始cwnd可以大致通過查看到:

  1. ss -itmpn dst "10.81.212.8" 
  2. State      Recv-Q Send-Q Local Address:Port  Peer Address:Port 
  3. ESTAB      0      0      10.xx.xx.xxx:22     10.yy.yy.yyy:12345  users:(("sshd",pid=1442,fd=3)) 
  4.          skmem:(r0,rb369280,t0,tb87040,f4096,w0,o0,bl0,d92) 
  5.  
  6. Here we can see this socket has Receive Buffer 369280 bytes, and Transmit Buffer 87040 bytes. 
  7. Keep in mind the kernel will double any socket buffer allocation for overhead. 
  8. So a process asks for 256 KiB buffer with setsockopt(SO_RCVBUF) then it will get 512 KiB buffer 
  9. space. This is described on man 7 tcp. 

初始窗口計算的代碼邏輯,重點在18行:

  1. /* TCP initial congestion window as per rfc6928 */ 
  2. #define TCP_INIT_CWND           10 
  3.  
  4.  
  5. /* 3. Try to fixup all. It is made immediately after connection enters 
  6.  *    established state. 
  7.  */ 
  8. void tcp_init_buffer_space(struct sock *sk) 
  9.         int tcp_app_win = sock_net(sk)->ipv4.sysctl_tcp_app_win; 
  10.         struct tcp_sock *tp = tcp_sk(sk); 
  11.         int maxwin; 
  12.  
  13.         if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK)) 
  14.                 tcp_sndbuf_expand(sk); 
  15.  
  16.         //初始最大接收窗口計算過程 
  17.         tp->rcvq_space.space = min_t(u32, tp->rcv_wnd, TCP_INIT_CWND * tp->advmss); 
  18.         tcp_mstamp_refresh(tp); 
  19.         tp->rcvq_space.time = tp->tcp_mstamp; 
  20.         tp->rcvq_space.seq = tp->copied_seq; 
  21.  
  22.         maxwin = tcp_full_space(sk); 
  23.  
  24.         if (tp->window_clamp >= maxwin) { 
  25.                 tp->window_clamp = maxwin; 
  26.  
  27.                 if (tcp_app_win && maxwin > 4 * tp->advmss) 
  28.                         tp->window_clamp = max(maxwin - 
  29.                                                (maxwin >> tcp_app_win), 
  30.                                                4 * tp->advmss); 
  31.         } 
  32.  
  33.         /* Force reservation of one segment. */ 
  34.         if (tcp_app_win && 
  35.             tp->window_clamp > 2 * tp->advmss && 
  36.             tp->window_clamp + tp->advmss > maxwin) 
  37.                 tp->window_clamp = max(2 * tp->advmss, maxwin - tp->advmss); 
  38.  
  39.         tp->rcv_ssthresh = min(tp->rcv_ssthresh, tp->window_clamp); 
  40.         tp->snd_cwnd_stamp = tcp_jiffies32; 

傳輸過程中,最大接收窗口會動態(tài)調(diào)整,當(dāng)指定了SO_RCVBUF后,實際buffer是兩倍SO_RCVBUF,但是要分出一部分(2^net.ipv4.tcp_adv_win_scale)來作為亂序報文緩存。

  1. net.ipv4.tcp_adv_win_scale = 2 //2.6內(nèi)核,3.1中這個值默認(rèn)是1。 

如果SO_RCVBUF是8K,總共就是16K,然后分出2^2分之一,也就是4分之一,還剩12K當(dāng)做接收窗口;如果設(shè)置的32K,那么接收窗口是48K。

  1. static inline int tcp_win_from_space(const struct sock *sk, int space
  2. {//space 傳入的時候就已經(jīng)是 2*SO_RCVBUF了 
  3.         int tcp_adv_win_scale = sock_net(sk)->ipv4.sysctl_tcp_adv_win_scale; 
  4.  
  5.         return tcp_adv_win_scale <= 0 ? 
  6.                 (space>>(-tcp_adv_win_scale)) : 
  7.                 space - (space>>tcp_adv_win_scale); //sysctl參數(shù)tcp_adv_win_scale 

接收窗口有最大接收窗口和當(dāng)前可用接收窗口。

一般來說一次中斷基本都會將 buffer 中的包都取走。

 

綠線是最大接收窗口動態(tài)調(diào)整的過程,最開始是1460*10,握手完畢后略微調(diào)整到1472*10(可利用body增加了12),隨著數(shù)據(jù)的傳輸開始跳漲。

 

上圖是四個batch insert語句,可以看到綠色接收窗口隨著數(shù)據(jù)的傳輸越來越大,圖中藍(lán)色豎直部分基本表示SQL上傳,兩個藍(lán)色豎直條的間隔代表這個insert在服務(wù)器上真正的執(zhí)行時間。這圖非常陡峭,表示上傳沒有任何瓶頸。

設(shè)置 SO_RCVBUF 后通過wireshark觀察到的接收窗口基本

下圖是設(shè)置了 SO_RCVBUF 為8192的實際情況:

 

從最開始的14720,執(zhí)行第一個create table語句后降到14330,到真正執(zhí)行batch insert就降到了8192*1.5. 然后一直保持在這個值。

If you set a "receive buffer size" on a TCP socket, what does it actually mean?

The naive answer would go something along the lines of: the TCP receive buffer setting indicates the maximum number of bytes a read() syscall could retrieve without blocking.

Note that if the buffer size is set with setsockopt(), the value returned with getsockopt() is always double the size requested to allow for overhead. This is described in man 7 socket.

總結(jié)

  • 一般來說絕對不要在程序中手工設(shè)置SO_SNDBUF和SO_RCVBUF,內(nèi)核自動調(diào)整比你做的要好;
  • SO_SNDBUF一般會比發(fā)送滑動窗口要大,因為發(fā)送出去并且ack了的才能從SO_SNDBUF中釋放;
  • TCP接收窗口跟SO_RCVBUF關(guān)系很復(fù)雜;
  • SO_RCVBUF太小并且rtt很大的時候會嚴(yán)重影響性能;
  • 接收窗口比發(fā)送窗口復(fù)雜多了;
  • 發(fā)送窗口/SO_SNDBUF--發(fā)送倉庫,帶寬/擁塞窗口--馬路通暢程度,接收窗口/SO_RCVBUF--接收倉庫;
  • 發(fā)送倉庫、馬路通暢程度、接收倉庫一起決定了傳輸速度--類比一下快遞過程。

總之記住一句話:不要設(shè)置socket的SO_SNDBUF和SO_RCVBUF。

 

責(zé)任編輯:武曉燕 來源: 阿里技術(shù)
相關(guān)推薦

2009-06-12 16:55:10

VPN客戶端故障

2014-03-18 14:11:07

SAPd-code

2014-05-22 09:23:33

Teradata 大數(shù)據(jù)天睿

2011-09-25 23:03:39

2013-05-07 20:53:44

2013GMGC移動開發(fā)手游開發(fā)

2010-12-08 09:37:53

Chrome OSGoogle

2013-07-25 14:44:31

ChinaJoy

2010-12-01 13:21:32

TechED2010

2009-10-12 15:26:28

2009 Oracle

2022-05-17 11:16:33

軟件開發(fā)優(yōu)化

2015-10-28 09:52:12

vForum2015

2010-12-02 13:41:26

SQL AzureTechED 2010

2020-02-26 11:45:05

RSACRSA大會綠盟科技

2014-07-16 18:47:05

2014-07-16 12:06:26

SyScan360

2014-11-25 11:03:36

2014WOT彈性計算

2021-09-17 16:00:54

智博會數(shù)字化

2009-11-24 10:21:46

2010-08-26 09:12:33

非法DHCP
點贊
收藏

51CTO技術(shù)棧公眾號