色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

沒有accept,能建立TCP連接嗎?

電子工程師 ? 來源:小林coding ? 作者:小林coding ? 2022-08-05 10:37 ? 次閱讀

正文

下面這個動圖,是我們平時客戶端和服務端建立連接時的代碼流程。

df3b975e-1464-11ed-ba43-dac502259ad0.gif握手建立連接流程

對應的是下面一段簡化過的服務端偽代碼。

intmain()
{
/*Step1:創建服務器端監聽socket描述符listen_fd*/
listen_fd=socket(AF_INET,SOCK_STREAM,0);

/*Step2:bind綁定服務器端的IP和端口,所有客戶端都向這個IP和端口發送和請求數據*/
bind(listen_fd,xxx);

/*Step3:服務端開啟監聽*/
listen(listen_fd,128);

/*Step4:服務器等待客戶端的鏈接,返回值cfd為客戶端的socket描述符*/
cfd=accept(listen_fd,xxx);

/*Step5:讀取客戶端發來的數據*/
n=read(cfd,buf,sizeof(buf));
}

估計大家也是老熟悉這段偽代碼了。

需要注意的是,在執行listen()方法之后還會執行一個accept()方法。

一般情況下,如果啟動服務器,會發現最后程序會阻塞在accept()里。

此時服務端就算ok了,就等客戶端了。

那么,再看下簡化過的客戶端偽代碼。

intmain()
{
/*Step1:創建客戶端端socket描述符cfd*/
cfd=socket(AF_INET,SOCK_STREAM,0);

/*Step2:connect方法,對服務器端的IP和端口號發起連接*/
ret=connect(cfd,xxxx);

/*Step4:向服務器端寫數據*/
write(cfd,buf,strlen(buf));
}

客戶端比較簡單,創建好socket之后,直接就發起connect方法。

此時回到服務端,會發現之前一直阻塞的accept方法,返回結果了

這就算兩端成功建立好了一條連接。之后就可以愉快的進行讀寫操作了。

那么,我們今天的問題是,如果沒有這個accept方法,TCP連接還能建立起來嗎?

其實只要在執行accept() 之前執行一個 sleep(20),然后立刻執行客戶端相關的方法,同時抓個包,就能得出結論。

df544aa6-1464-11ed-ba43-dac502259ad0.png不執行accept時抓包結果

從抓包結果看來,就算不執行accept()方法,三次握手照常進行,并順利建立連接。

更騷氣的是,在服務端執行accept()前,如果客戶端發送消息給服務端,服務端是能夠正常回復ack確認包的。

并且,sleep(20)結束后,服務端正常執行accept(),客戶端前面發送的消息,還是能正常收到的。

通過這個現象,我們可以多想想為什么。順便好好了解下三次握手的細節。

三次握手的細節分析

我們先看面試八股文的老股,三次握手。

df63c2ce-1464-11ed-ba43-dac502259ad0.pngTCP三次握手

服務端代碼,對socket執行bind方法可以綁定監聽端口,然后執行listen方法后,就會進入監聽(LISTEN)狀態。內核會為每一個處于LISTEN狀態的socket 分配兩個隊列,分別叫半連接隊列和全連接隊列

df74a92c-1464-11ed-ba43-dac502259ad0.png每個listen Socket都有一個全連接和半連接隊列

半連接隊列、全連接隊列是什么

df826e68-1464-11ed-ba43-dac502259ad0.png半連接隊列和全連接隊列
  • 半連接隊列(SYN隊列),服務端收到第一次握手后,會將sock加入到這個隊列中,隊列內的sock都處于SYN_RECV 狀態。
  • 全連接隊列(ACCEPT隊列),在服務端收到第三次握手后,會將半連接隊列的sock取出,放到全連接隊列中。隊列里的sock都處于 ESTABLISHED狀態。這里面的連接,就等著服務端執行accept()后被取出了。

看到這里,文章開頭的問題就有了答案,建立連接的過程中根本不需要accept() 參與, 執行accept()只是為了從全連接隊列里取出一條連接。

我們把話題再重新回到這兩個隊列上。

雖然都叫隊列,但其實全連接隊列(icsk_accept_queue)是個鏈表,而半連接隊列(syn_table)是個哈希表

df8f7cd4-1464-11ed-ba43-dac502259ad0.png半連接全連接隊列的內部結構

為什么半連接隊列要設計成哈希表

先對比下全連接里隊列,他本質是個鏈表,因為也是線性結構,說它是個隊列也沒毛病。它里面放的都是已經建立完成的連接,這些連接正等待被取走。而服務端取走連接的過程中,并不關心具體是哪個連接,只要是個連接就行,所以直接從隊列頭取就行了。這個過程算法復雜度為O(1)

半連接隊列卻不太一樣,因為隊列里的都是不完整的連接,嗷嗷等待著第三次握手的到來。那么現在有一個第三次握手來了,則需要從隊列里把相應IP端口的連接取出,如果半連接隊列還是個鏈表,那我們就需要依次遍歷,才能拿到我們想要的那個連接,算法復雜度就是O(n)。

而如果將半連接隊列設計成哈希表,那么查找半連接的算法復雜度就回到O(1)了。

因此出于效率考慮,全連接隊列被設計成鏈表,而半連接隊列被設計為哈希表。

怎么觀察兩個隊列的大小

查看全連接隊列

#ss-lnt
StateRecv-QSend-QLocalAddress:PortPeerAddress:Port
LISTEN0128127.0.0.1:46269*:*

通過ss -lnt命令,可以看到全連接隊列的大小,其中Send-Q是指全連接隊列的最大值,可以看到我這上面的最大值是128Recv-Q是指當前的全連接隊列的使用值,我這邊用了0個,也就是全連接隊列里為空,連接都被取出來了。

當上面Send-QRecv-Q數值很接近的時候,那么全連接隊列可能已經滿了。可以通過下面的命令查看是否發生過隊列溢出

#netstat-s|grepoverflowed
4343timesthelistenqueueofasocketoverflowed

上面說明發生過4343次全連接隊列溢出的情況。這個查看到的是歷史發生過的次數

如果配合使用watch -d 命令,可以自動每2s間隔執行相同命令,還能高亮顯示變化的數字部分,如果溢出的數字不斷變多,說明正在發生溢出的行為。

#watch-d'netstat-s|grepoverflowed'
Every2.0s:netstat-s|grepoverflowedFriSep1709452021

4343timesthelistenqueueofasocketoverflowed

查看半連接隊列

半連接隊列沒有命令可以直接查看到,但因為半連接隊列里,放的都是SYN_RECV 狀態的連接,那可以通過統計處于這個狀態的連接的數量,間接獲得半連接隊列的長度。

#netstat-nt|grep-i'127.0.0.1:8080'|grep-i'SYN_RECV'|wc-l
0

注意半連接隊列和全連接隊列都是掛在某個Listen socket上的,我這里用的是127.0.0.1:8080,大家可以替換成自己想要查看的IP端口

可以看到我的機器上的半連接隊列長度為0,這個很正常,正經連接誰會沒事老待在半連接隊列里。

當隊列里的半連接不斷增多,最終也是會發生溢出,可以通過下面的命令查看。

#netstat-s|grep-i"SYNstoLISTENsocketsdropped"
26395SYNstoLISTENsocketsdropped

可以看到,我的機器上一共發生了26395次半連接隊列溢出。同樣建議配合watch -d 命令使用。

#watch-d'netstat-s|grep-i"SYNstoLISTENsocketsdropped"'
Every2.0s:netstat-s|grep-i"SYNstoLISTENsocketsdropped"FriSep1708382021

26395SYNstoLISTENsocketsdropped

全連接隊列滿了會怎么樣?

如果隊列滿了,服務端還收到客戶端的第三次握手ACK,默認當然會丟棄這個ACK。

但除了丟棄之外,還有一些附帶行為,這會受 tcp_abort_on_overflow 參數的影響。

#cat/proc/sys/net/ipv4/tcp_abort_on_overflow
0
  • tcp_abort_on_overflow設置為 0,全連接隊列滿了之后,會丟棄這個第三次握手ACK包,并且開啟定時器,重傳第二次握手的SYN+ACK,如果重傳超過一定限制次數,還會把對應的半連接隊列里的連接給刪掉。
df9c5cc4-1464-11ed-ba43-dac502259ad0.pngtcp_abort_on_overflow為0
  • tcp_abort_on_overflow設置為 1,全連接隊列滿了之后,就直接發RST給客戶端,效果上看就是連接斷了。

這個現象是不是很熟悉,服務端端口未監聽時,客戶端嘗試去連接,服務端也會回一個RST。這兩個情況長一樣,所以客戶端這時候收到RST之后,其實無法區分到底是端口未監聽,還是全連接隊列滿了

dfaa62a6-1464-11ed-ba43-dac502259ad0.pngtcp_abort_on_overflow為1

半連接隊列要是滿了會怎么樣

一般是丟棄,但這個行為可以通過 tcp_syncookies 參數去控制。但比起這個,更重要的是先了解下半連接隊列為什么會被打滿。

首先我們需要明白,一般情況下,半連接的"生存"時間其實很短,只有在第一次和第三次握手間,如果半連接都滿了,說明服務端瘋狂收到第一次握手請求,如果是線上游戲應用,能有這么多請求進來,那說明你可能要富了。但現實往往比較骨感,你可能遇到了SYN Flood攻擊

所謂SYN Flood攻擊,可以簡單理解為,攻擊方模擬客戶端瘋狂發第一次握手請求過來,在服務端憨憨地回復第二次握手過去之后,客戶端死活不發第三次握手過來,這樣做,可以把服務端半連接隊列打滿,從而導致正常連接不能正常進來。

dfcd5ec8-1464-11ed-ba43-dac502259ad0.pngsyn攻擊

那這種情況怎么處理?有沒有一種方法可以繞過半連接隊列

有,上面提到的tcp_syncookies派上用場了。

#cat/proc/sys/net/ipv4/tcp_syncookies
1

當它被設置為1的時候,客戶端發來第一次握手SYN時,服務端不會將其放入半連接隊列中,而是直接生成一個cookies,這個cookies會跟著第二次握手,發回客戶端。客戶端在發第三次握手的時候帶上這個cookies,服務端驗證到它就是當初發出去的那個,就會建立連接并放入到全連接隊列中。可以看出整個過程不再需要半連接隊列的參與。

dfd804cc-1464-11ed-ba43-dac502259ad0.pngtcp_syncookies=1

會有一個cookies隊列嗎

生成是cookies,保存在哪呢?是不是會有一個隊列保存這些cookies?

我們可以反過來想一下,如果有cookies隊列,那它會跟半連接隊列一樣,到頭來,還是會被SYN Flood 攻擊打滿。

實際上cookies并不會有一個專門的隊列保存,它是通過通信雙方的IP地址端口、時間戳、MSS信息進行實時計算的,保存在TCP報頭seq里。

dff73630-1464-11ed-ba43-dac502259ad0.pngtcp報頭_seq的位置

當服務端收到客戶端發來的第三次握手包時,會通過seq還原出通信雙方的IP地址端口、時間戳、MSS,驗證通過則建立連接。

cookies方案為什么不直接取代半連接隊列?

目前看下來syn cookies方案省下了半連接隊列所需要的隊列內存,還能解決 SYN Flood攻擊,那為什么不直接取代半連接隊列?

凡事皆有利弊,cookies方案雖然能防 SYN Flood攻擊,但是也有一些問題。因為服務端并不會保存連接信息,所以如果傳輸過程中數據包丟了,也不會重發第二次握手的信息。

另外,編碼解碼cookies,都是比較CPU的,利用這一點,如果此時攻擊者構造大量的第三次握手包(ACK包),同時帶上各種瞎編的cookies信息,服務端收到ACK包以為是正經cookies,憨憨地跑去解碼(耗CPU),最后發現不是正經數據包后才丟棄。

這種通過構造大量ACK包去消耗服務端資源的攻擊,叫ACK攻擊,受到攻擊的服務器可能會因為CPU資源耗盡導致沒能響應正經請求。

e0037ae4-1464-11ed-ba43-dac502259ad0.gifack攻擊

沒有listen,為什么還能建立連接

那既然沒有accept方法能建立連接,那是不是沒有listen方法,也能建立連接?是的,之前寫的一篇文章提到過客戶端是可以自己連自己的形成連接(TCP自連接),也可以兩個客戶端同時向對方發出請求建立連接(TCP同時打開),這兩個情況都有個共同點,就是沒有服務端參與,也就是沒有listen,就能建立連接。

當時文章最后也留了個疑問,沒有listen,為什么還能建立連接?

我們知道執行listen方法時,會創建半連接隊列和全連接隊列。

三次握手的過程中會在這兩個隊列中暫存連接信息。

所以形成連接,前提是你得有個地方存放著,方便握手的時候能根據IP端口等信息找到socket信息。

那么客戶端會有半連接隊列嗎?

顯然沒有,因為客戶端沒有執行listen,因為半連接隊列和全連接隊列都是在執行listen方法時,內核自動創建的。

但內核還有個全局hash表,可以用于存放sock連接的信息。這個全局hash表其實還細分為ehash,bhash和listen_hash等,但因為過于細節,大家理解成有一個全局hash就夠了,

在TCP自連接的情況中,客戶端在connect方法時,最后會將自己的連接信息放入到這個全局hash表中,然后將信息發出,消息在經過回環地址重新回到TCP傳輸層的時候,就會根據IP端口信息,再一次從這個全局hash中取出信息。于是握手包一來一回,最后成功建立連接。

TCP 同時打開的情況也類似,只不過從一個客戶端變成了兩個客戶端而已。

總結

  • 每一個socket執行listen時,內核都會自動創建一個半連接隊列和全連接隊列。
  • 第三次握手前,TCP連接會放在半連接隊列中,直到第三次握手到來,才會被放到全連接隊列中。
  • accept方法只是為了從全連接隊列中拿出一條連接,本身跟三次握手幾乎毫無關系
  • 出于效率考慮,雖然都叫隊列,但半連接隊列其實被設計成了哈希表,而全連接隊列本質是鏈表。
  • 全連接隊列滿了,再來第三次握手也會丟棄,此時如果tcp_abort_on_overflow=1,還會直接發RST給客戶端。
  • 半連接隊列滿了,可能是因為受到了SYN Flood攻擊,可以設置tcp_syncookies,繞開半連接隊列。
  • 客戶端沒有半連接隊列和全連接隊列,但有一個全局hash,可以通過它實現自連接或TCP同時打開。
審核編輯 :李倩


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 服務器
    +關注

    關注

    12

    文章

    9123

    瀏覽量

    85328
  • TCP
    TCP
    +關注

    關注

    8

    文章

    1353

    瀏覽量

    79055
  • 代碼
    +關注

    關注

    30

    文章

    4779

    瀏覽量

    68525

原文標題:阿里二面:沒有 accept,能建立 TCP 連接嗎?

文章出處:【微信號:小林coding,微信公眾號:小林coding】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    TCP和UDP建立連接的差異和可靠性的差異

    TCP 建立連接需要經過三次握手,同時 TCP 斷開連接需要經過四次揮手,這也表示 TCP 是一
    發表于 09-02 16:10 ?793次閱讀

    如何標識一個TCP連接

    tcp應用中,server事先在某個固定端口監聽,client主動發起連接,經過三路握手后建立tcp連接。那么對單機,其最大并發
    的頭像 發表于 10-10 10:33 ?2946次閱讀

    STM32H7+FREERTOS+LWIP建立TCP連接連接不穩定怎么解決?

    利用ST的Demo建立TCP連接,但是當建立TCP連接超過4個時,
    發表于 04-25 06:05

    6678的sy***ios中的NDK的例程hello和clint中,使用TCP沒有看到bing和listen accept等函數

    6678的sy***ios中的NDK的例程hello和clint中,使用TCP沒有看到bing和listen accept等函數,請問為什么沒有
    發表于 06-21 15:46

    為什么建立TCP連接有時成功有時失敗?

    時開發板自帶的里程改的,TCP這部分基本沒有改,只是增加了自己的應用進去。首次使用以太網接口,還不太清楚建立連接的機理,有了解這塊的朋友嗎,請問建立
    發表于 09-19 04:36

    配置靜態IP地址時,沒有TCP連接可以建立,對ping沒有響應

    新的,并且我正在嘗試使用上面提到的具有集成PHY,25MHz的控制器來制造小型TCP服務器。外部OSC,沒有外部的32 kHz OSC。我面臨的幾個問題是:1。當配置靜態IP地址時,沒有TCP
    發表于 11-04 07:03

    RT-Thread socket編程無法建立TCP連接是何原因

    腦socket調試工具無法建立TCP連接的問題,代碼如下:SOCKET s,s2;struct sockaddr_in local_addr,foreign_addr;int len;s
    發表于 04-25 17:14

    TCP連接建立與終止

    學習TCP-IP的很好的書。TCP-IP詳解卷1。
    發表于 05-10 15:44 ?0次下載

    大神告訴你TCP建立連接為什么是三次握手

    所謂三次握手(Three-Way Handshake)即建立TCP連接,是指建立一個TCP連接
    的頭像 發表于 04-16 11:43 ?1w次閱讀
    大神告訴你<b class='flag-5'>TCP</b><b class='flag-5'>建立</b><b class='flag-5'>連接</b>為什么是三次握手

    TCP通信通過網絡調試助手與S7-1200建立TCP連接

    S7-1200 V4.5 版本開始支持網絡視圖組態開放式用戶通信連接,不需要在程序中調用 TCON 等建立連接指令,只需要調用發送接收指令即可實現數據的收發。下面以 TCP 通信為例,
    的頭像 發表于 07-06 15:39 ?1.2w次閱讀

    要是沒有一端進行監聽是否可以建立TCP連接呢?

    TCP 網絡通信過程中,我們都是先有 server 端調用 listen 監聽某個端口號,然后 client 向 server 發起連接請求,最終建立連接
    的頭像 發表于 11-16 17:42 ?2156次閱讀
    要是<b class='flag-5'>沒有</b>一端進行監聽是否可以<b class='flag-5'>建立</b>起<b class='flag-5'>TCP</b><b class='flag-5'>連接</b>呢?

    什么是Socket連接?Socket與TCP連接的關系

    主機 A 的應用程序必須通過 Socket 建立連接才能與主機B的應用程序通信,而建立 Socket 連接需要底層 TCP/IP 協議來
    發表于 03-31 15:10 ?1041次閱讀

    TCP連接建立與中止

    TCP三次握手其實,網絡上的傳輸是沒有連接的, TCP 是一樣的 TCP 所謂的 “連接”,其實
    的頭像 發表于 10-08 16:52 ?729次閱讀

    TCP連接建立中的異常

    ? ? 建連接時SYN超時問題 如果 server 端因為某種情況沒有收到 client 回來的 ACK,那么,這個連接處還處于一個未建立的狀態。于是,server端如果在一定時間內
    的頭像 發表于 10-08 17:01 ?788次閱讀

    TCP的長連接和短連接

    TCP在真正開始進行數據傳輸之前,Server 和 Client 之間必須建立一個連接。當數據傳輸完成后,雙方不再需要這個連接時,就可以釋放這個連接
    的頭像 發表于 11-13 10:46 ?1011次閱讀
    主站蜘蛛池模板: 欧美精品高清在线观看| 爱人 qvod| 亚洲视频区| 忘忧草高清| 丝瓜涩涩屋黄瓜香蕉丝瓜| 毛片在线播放网址| 麻豆精品传媒一二三区| 久久日本精品国产精品| 精品麻豆一卡2卡三卡4卡乱码| 国产免费69成人精品视频| 国产人妻人伦精品A区| 国产精品久久久久婷婷五月色婷婷| WRITEAS检查身体| yellow免费影视大全| yy8090韩国理伦片在线| 饱满奶大30p| 久久人妻熟女中文字幕AV蜜芽| 精品国产原创在线观看视频| 精品美女国产互换人妻| 久久re这里视频精品8| 久久看片网| 暖暖视频免费高清在线观看 视频| 美国色情三级欧美三级纸匠情挑| 嫩草影院在线观看网站成人| 欧美整片华人play| 人妻激情综合久久久久蜜桃| 日本黄色网站在线观看| 天美传媒在线完整免费观看网站| 四库影院永久国产精品| 亚洲高清国产拍精品5g| 一本二卡三卡四卡乱码麻豆| 综合激情区视频一区视频二区| 51xx午夜影视福利| www.一级毛片| 国产高清视频在线观看97| 国模孕妇模特季玥之粉红| 狠日狠干日曰射| 免费三级黄色| 日日日操操操| 亚洲乱码爆乳精品成人毛片| 2224x最新网站|