後端網絡編程知識點總結

這一篇是網絡編程的面試知識點的總結。不打算從計算機網絡的基礎講起,只是摘要性的總結一下知識點,更多的內容請從經典的書籍中獲取。

1、tcp與udp的區別(必問)

  • 可靠性

      首先,TCP是一個面向連接的協議,需要三次握手。具有可靠性,但是並不是百分百的可靠,它通過序列號確認,超時重傳等機制提供數據的可靠傳送或者故障的可靠通知,但並不能保障數據一定會被對方接收。
      UDP是一個無連接的協議。UDP不保證數據報會到達最終的目的地,不保證數據報的先後順序不變,不保證每個數據報只到達一次。不會自動的重傳以及報告錯誤。

  • 協議類型

      每個UDP數據報都有一個長度,改數據報的長度隨數據一道傳遞給接收端應用程序,而TCP是一個字節流協議,沒有任何的記錄邊界。TCP和UDP都可以IPv4和IPv6。
      

  • 流量控制

      TCP提供流量控制,TCP總是告知對端在任何時刻它一次能夠從對端接收多少字節的數據。從而保證發送端的發送的數據不會使接收緩衝區溢出。接收端的窗口大小會動態的變化。UDP不提供流量控制。

  • 全雙工

      TCP連接是全雙工的,意味着連接上的應用可以在任何時刻既發送數據又接收數據。需要的話也可轉化成單工的連接。UDP**可以**是全雙工的。
     
      比較來看,UDP似乎不是很好的選擇,但是它勝在速度快(不需要建立連接),佔用資源少(不需要狀態維護)。報文的首部開銷小,8個字節,TCP首部20個字節。從實際應用來說,也會選擇UDP模擬實現TCP,或者使用上層協議保證傳輸可靠。

2、udp調用connect有什麼作用?

  UDP的connect和TCP的connect不是一個概念,沒有三次握手的過程。對於使用connect的已經連接的UDP套接字,與默認的未連接的套接字相比:
  

  1. 再也不能給輸出操作指定目的地的IP和端口號。寫到已連接UDP套接字上的任何內容都會自動發送到connect指定的協議地址上。使用write或send代替sendto。

  2. 不必使用recvfrom獲悉數據報發送者,因爲從內核返回的數據報只有那些指定協議地址的數據報。改用read,recv,recvmsg。

  3. 由已連接UDP套接字引發的異步錯誤會返回給它們所在的進程。而未連接套接字不接收任何異步錯誤。

    總的來說,connect啓動一個聲明和限定通信方的作用。同時,更少次數的連接和斷開可以提高數據的傳輸效率。

    如果對一個已連接的UDP套接字進程再次調用connect,可以:

    1.指定新的IP地址和端口。

    2.斷開套接字。

3、tcp連接中時序圖,狀態圖,必須非常非常熟練

這個沒什麼好說的,看圖。
這裏寫圖片描述

這裏寫圖片描述

4、socket服務端的實現,select和epoll的區別(必問)

  基本的socket服務端實現就在上面的圖上。
  epoll的出現比較晚了,epoll是linux獨有的高效的IO複用的機制。先簡單提一下poll和select,poll和select很相似,但是poll沒有select的文件描述符的限制(默認1024),poll使用pollfd結構代替描述符fd。
  而epoll與poll的不同在於:poll每次返回整個文件描述符的數組,用戶代碼需要遍歷數組找到哪些文件描述符上有IO事件。而epoll_wait返回的是活動fd的列表,比遍歷的數組小的多。epoll使用的是一種註冊回調的機制,epoll_ctl爲每一個fd指定一個回調函數,當設備就緒的時候,會喚醒等待隊列中的等候者並調用其回調函數,回調函數會把對應的fd加入到就緒隊列,epoll _wait就負責查詢這個就緒隊列,同時epoll也沒有文件描述符數目的限制。
  select和poll就相當於輪詢,需要遍歷整個的fd集合來確定IO就緒,而epoll只需要判斷一下就緒鏈表是否爲空就可以了。

struct pollfd{
    int fd;
    short events;
    short revents;
};

5、epoll哪些觸發模式,有啥區別?(必須非常詳盡的解釋水平觸發和邊緣觸發的區別,以及邊緣觸發在編程中要做哪些更多的確認)

  如果稍微有點電子方面經驗的人,相信對水平觸發和邊沿觸發的概念就比較瞭解了。如果是邊緣觸發模式,就是當狀態變化的時候才通知,對應電子中的高低電平變化的上升沿和下降沿。而水平觸發則是你等待的狀態到了,就一直通知。
  這裏寫圖片描述
  注意因爲邊沿觸發在被監控的文件描述符上有可讀寫事件發生時,epoll_wait會通知處理程序去讀寫,但是它只會通知一次。如果沒有把數據全部讀寫完,下次調用epoll_wait時並不會通知你。因此需要去確認緩衝區中的數據讀寫完畢,例如在非阻塞模式下讀取數據,使用循環。根據返回的error,EAGIN和EWOULDBLOCK來判斷數據是否全部讀取完畢。
  

6、大規模連接上來,併發模型怎麼設計

  我本身並沒有做過大規模連接的項目,說併發模型設計屬於紙上談兵。
  如果沒有這方面概念的,先看一看C10K問題。
  http://www.kegel.com/c10k.html#frameworks
  比較通俗易懂的知乎專欄:
  https://zhuanlan.zhihu.com/p/20311080
  《unix網絡編程》第30章有關於服務器設計的範式,雖然內容稍顯過時。
  現在應用比較廣泛的應該是多進程/多線程+“non-blocking IO+IO multiplexing(對應Java的NIO概念)”的異步IO模型,使用Linux的epoll或者kqueue,或者使用libevent封裝的統一接口。基本結構就是一個事件循環(event loop),以事件驅動(event-driven)和事件回調方式實現業務邏輯。另外對於CPU密集型的操作,可以開線程池作爲補充。
  相關代碼和實現可以參考nignx。
  http://tengine.taobao.org/book/chapter_02.html#id1

7、tcp結束連接怎麼握手,time_wait狀態是什麼,爲什麼會有time_wait狀態?哪一方會有time_wait狀態,如何避免time_wait狀態佔用資源(必須回答的詳細)

  這裏問的應該是怎麼揮手。以下部分摘自《unix網絡編程》和《TCP/IP詳解卷一》。首先TCP終止連接需要4個分節。因爲TCP是一個全雙工的連接,每個方向都必須單獨關閉。
 
- 某個應用進程首先調動close,主動關閉,發送一個FIN(意味着這一個方向沒有數據的流動)字節。
- 接收到這個FIN的對端被動關閉。這個FIN由TCP確認(ACK),並作爲一個文件結束符傳遞給接收端應用程序。
- 一段時間後,接收到這個結束符的應用程序調用close關閉它的套接字。這導致它的TCP也發送一個FIN。
- 接收這個最終FIN的原發送端TCP確認這個FIN。

  這裏寫圖片描述
  注意,主動關閉方會有一個TIME_WAIT狀態,也稱爲2MSL狀態。主要是防止新建立的連接使用原來的socket,遲到的報文到達後被錯誤的接收。MSL(maximum Segment Lifetime)是任何報文段被丟棄在網絡內的最長時間。在2MSL時間內,本次連接的socket(客戶IP和端口,服務器IP和端口)不能再被使用。遲到的報文會被丟棄。
  TIME_WAIT的另一個存在理由是可靠的實現TCP全雙工連接的終止。假設最終的ACK丟失了,服務器需要重新發送一個FIN,客戶必須能夠重新發送ACK,不然它將響應以一個RST,這將被服務器解釋爲一個錯誤。
  大量的TCP的短連接引發的TIME_WAIT會佔用很多資源,其默認等待時間比較保守。
  
- 可以通過修改操作系統設置例如tcp_fin_timeout減少TIME_WAIT時間;
- 設置tcp_tw_recycle=1開啓socket的快速回收;
- 使用比較激進的socket的SO_LINGER選項,設置爲0,取消關閉等待。
- 或者選擇使用tcp_tw_reuse開啓socket的重用。

8、tcp頭多少字節?哪些字段?(必問)

這裏寫圖片描述

  TCP頭有固定的20字節,並且有可擴充的選項。每個TCP段都包含用於尋找發送端和接收端的源端口和目的端口。這兩個值加上IP首部的源IP地址和目的IP地址能夠確定唯一的一個TCP連接。
  序號表示這個數據報段的第一個數據字節。用來標識從TCP發送端到接收端的數據字節流。
  確認號包含發送確認的一端期望接收的下一個序號。爲上次接收成功的數據字節序加1。ACK標誌位1時確認號的字段纔有效。一旦連接建立,ACK標誌字段總是設置爲1。
  這裏數據偏移表示有誤,實際上就是TCP首部的長度。以32位爲長度單位,偏移的意思是數據開始的地方離TCP段的起始位置的距離。
  下面是6個標誌比特位:
  URG:緊急指針有效,用以通知另一端緊急數據已經放置在普通的數據流中,僅僅是通知而已,具體如何處理由接收端決定。注意後面16位的緊急指針是一個正的偏移量,通過與TCP首部序號字段相加,得出緊急數據最後一個字節的序號。但是沒有辦法指明緊急數據從數據流的何處開始。TCP通過連接傳送的唯一信息時緊急方式已經開始,以及結束位置。接收方當前讀取位置到緊急指針之間的數據會使應用程序處於緊急方式,通過緊急指針後,恢復正常方式。
  ACK:確認序列號有效。
  PSH:接收方應該儘快將這個報文段交給應用層。意思是當TCP向服務器發送一個報文段時,不要因等待額外數據而使已提交的數據在緩存中滯留。TCP的一些算法實現會推遲數據的交付,例如等待緩衝區填滿。
  RST:重建連接。
  SYN:同步序號用來發起一個連接。既三次握手的同步。
  FIN:發送端完成發送任務,常見於連接斷開,四次揮手。

  校驗和覆蓋整個TCP報文段,首部加數據。由發送端計算和存儲,由接收端驗證。
  具體的實現由RFC793定義。
  首先,把僞首部、TCP報頭、TCP數據分爲16位的字,如果總長度爲奇數個字節,則在最後增添一個位都爲0的字節。把TCP報頭中的校驗和字段置爲0。
  其次,用反碼相加法累加所有的16位字(進位也要累加)。
  最後,對計算結果取反,作爲TCP的校驗和。
  
  既發送方的原碼相加,將高位疊加到低位,取反放入校驗和段中。
  接收方將所有數據原碼相加,高位疊加帶低位,如果全爲1,則正確。
  選項在後面有詳述。

9、什麼是滑動窗口(必問)

  滑動窗口協議是應流量控制方法。該協議允許發送方在停止並等待確認前可以連續發送多個分組。加速數據的傳輸。發送方不必發送一個全窗口大小的數據,具體如圖:
  這裏寫圖片描述
  如上圖所示,對於TCP的發送方,在發送緩存中的數據可分爲4個部分。已發送並被確認的,既已經收到對端ACK的數據,已發送但是未被確認的數據,未發送到但對端允許發送的數據,以及最後不可發送的數據。
  滑動窗口由接收方通告,限制發送方的發送速度。其主要有兩個作用:一是提供TCP的可靠性,二是控制流量。TCP的首部中有相關的字段window,上面已經介紹過了。
  注意ACK是期望收到的下一個字節的序號n,並表示接收方已經收到了前n-1個字節,假如接收端收到1-1024個字節,它會發送一個1025的確認號,如果接下來收到的是2049-3072,它是不會接着發送3073的ACK的,而是接着發送1025。
  通過窗口大小m和發送方接收到的ACK序列號n可以確認還可以發送多少數據,假設發送到第x字節,既可發送和不可發送的交界。則可以發送的字節數位y=m-(x-n)。通過調整窗口大小就可以做到流量的控制。
  當數據被髮送和確認時,滑動窗口的左邊沿向右移動,窗口合攏。當接收端讀取已經確認的數據並釋放TCP緩存時,窗口的右邊沿向右移動,窗口張開。

10、connect會阻塞,怎麼解決?(必考必問,提示:設置非阻塞,返回之後用select檢測狀態)

  這個問題已經把答案說出來了。通過設置TCP套接字爲非阻塞,並調用connect,connect會立即返回一個EINPROGRESS錯誤,但是已經發起的TCP三路握手繼續進行。然後接着使用select檢測連接的成功或者失敗。
  使用select和非阻塞connect,當連接成功建立的時候,描述符變爲可寫。當建立連接遇到錯誤的時候,描述符變爲既可讀又可寫。下一個問題會更多的講到select的描述符問題。
  使用非阻塞的connect有三個用途。
  

  1. 可以把三路握手疊加在其他處理上。既完成connect的時間內執行其他工作。
  2. 同時建立多個連接。
  3. 使用select等待連接建立,可以設置時間限制。縮短connect的超時。

11、如果select返回可讀,結果只讀到0字節,什麼情況?

  select函數的細節問題。先說答案吧,讀到0表示該連接的讀半部關閉(接收了FIN的TCP連接)。
  對於select函數返回套接字就緒的情況。
  可讀:
   1. 該套接字接收緩衝區的數據大於等於接收緩衝區低水位標記的當前大小。對此套接字執行讀操作將不阻塞並返回一個>0的值。使用SO_REVLOWAT套接字選項設置低水位標記。默認爲1。
   2. 該連接讀半部關閉,對此套接字讀將不阻塞並返回0。(EOF)
   3. 其上有一個錯誤待處理。對此套接字讀不阻塞返回-1,並把errno設置成確切的錯誤條件。
   4. 該套接字是一個監聽套接字並已完成的連接數不爲0。

  可寫:
  1. 發送緩衝區可用空間字節數大於等於低水位標記,且改套接字已連接,或不需要連接(UDP)。寫操作不阻塞返回正值。默認標記2048。
  2 . 該連接寫半部關閉,寫操作將產生SIGPIPE信號。
  3 . 使用非阻塞connect套接字連接已建立,或者已失敗。
  4 . 其上有一個錯誤待處理。同讀。

12、keepalive 是什麼東東?如何使用?

  在《TCP/IP詳解卷一》二十三章有對保活概念的描述。許多時候服務器希望知道客戶是否崩潰關機或者崩潰重新啓動。以防止服務器永遠等待一個消失的客戶。
  TCP的套接字中有SO_KEEPALIVE選項,設置後,如果兩小時內該套接字的任一方向都沒有數據交換,TCP就會自動給對端發送一個探測分節。這時對端必須響應一個TCP分節。有以下三種情況:
  1.對端返回以期望的ACK,應用進程因爲一切正常得不到通知。在又經過無動靜的2小時後,TCP再發另一個探測分節。
  2.對端以RST響應,告知本端,對端已崩潰並重啓。改套接字待處理錯誤被置爲ECONNRESET,套接字被關閉。
  3.對端無任何響應,將另外發送若干探測分節(Berkeley爲8),兩兩相隔75秒。在發送第一個分節11分15後無任何響應則放棄。待處理錯誤爲ETIMEOUT。套接字被關閉。若收到ICMP錯誤的響應,則返回相應的錯誤。套接字被關閉。

  KeepAlive並不是默認開啓的,在Linux系統上沒有一個全局的選項去開啓TCP的KeepAlive。需要開啓KeepAlive的應用必須在TCP的socket中單獨開啓。Linux Kernel有三個選項影響到KeepAlive的行爲:

1.net.ipv4.tcpkeepaliveintvl = 75
2.net.ipv4.tcpkeepaliveprobes = 9
3.net.ipv4.tcpkeepalivetime = 7200

  tcpkeepalivetime的單位是秒,表示TCP鏈接在多少秒之後沒有數據報文傳輸啓動探測報文; tcpkeepaliveintvl單位是也秒,表示前一個探測報文和後一個探測報文之間的時間間隔,tcpkeepaliveprobes表示探測的次數。

  TCP socket也有三個選項和內核對應,通過setsockopt系統調用針對單獨的socket進行設置:

1.TCPKEEPCNT: 覆蓋 tcpkeepaliveprobes
2.TCPKEEPIDLE: 覆蓋 tcpkeepalivetime
3.TCPKEEPINTVL: 覆蓋 tcpkeepalive_intvl

  注意HTTP上也有keep-alive的概念,它與TCP上的完全不是一回事兒。HTTP上的keep-alive是爲了複用TCP連接,以免每一個HTTP請求都新建並斷開一個TCP,提高傳輸效率。

13、列舉你所知道的tcp選項,並說明其作用。

這裏寫圖片描述

  kind表示類型,len爲總長度。常見選項7種。
  kind0:選項表結束。表示首部無更多消息。
  kind1:無操作,用來填充字段爲4字節的倍數。
  kind2:MSS最大報文長度。
  kind3:窗口擴大因子。窗口大小爲原始的N*(1<<移位數)。
  kind4:SACK,正常某個報文段丟失後會將其後所有報文重傳,設置SACK後只重傳丟失部分。
  kind5:SACK具體部分,發送端據此重傳。左邊沿爲不連續塊的第一個數據序號,右邊沿爲最後一個不連續塊數據序號的下一個序號。
  kind8:時間戳。經常打log的應該都瞭解。

14、socket什麼情況下可讀?

見11.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章