【網路程式設計】TCPIP-8-套接字的多種選項

李柱明發表於2021-08-25


前言

說明:

  • demo 基於 Linux。

8. 套接字的多種選項

一般建立好套接字後直接使用即可,有些配置是預設的,當然也可以通過以下修改。

8.1 API getsockopt(); & setsockopt();

/*
sock: 用於檢視選項套接字檔案描述符
level: 要檢視的可選項協議層
optname: 要檢視的可選項名
optval: 儲存檢視結果的緩衝地址值
optlen: 向第四個引數傳遞的緩衝大小。呼叫函式候,該變數中儲存通過第四個引數返回的可選項資訊的位元組數。
成功時返回 0 ,失敗時返回 -1
*/
#include <sys/socket.h>
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
/*
sock: 用於更改選項套接字檔案描述符
level: 要更改的可選項協議層
optname: 要更改的可選項名
optval: 儲存更改結果的緩衝地址值
optlen: 向第四個引數傳遞的緩衝大小。呼叫函式候,該變數中儲存通過第四個引數返回的可選項資訊的位元組數。
成功時返回 0 ,失敗時返回 -1
*/
#include <sys/socket.h>
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

8.2 套接字選項

注意,套接字可選項是分層的。

  • SOL_SOCKET:套接字的通用可選項。
  • IPPROTO_TCP:可選項是 TCP 協議的相關事項。
  • IPPROTO_IP:IP 協議相關事項。
協議層 選項名 讀取 設定
SOL_SOCKET SO_SNDBUF O O
SOL_SOCKET SO_RCVBUF O O
SOL_SOCKET SO_REUSEADDR O O
SOL_SOCKET SO_KEEPALIVE O O
SOL_SOCKET SO_BROADCAST O O
SOL_SOCKET SO_DONTROUTE O O
SOL_SOCKET SO_OOBINLINE O O
SOL_SOCKET SO_ERROR O X
SOL_SOCKET SO_TYPE O X
IPPROTO_IP IP_TOS O O
IPPROTO_IP IP_TTL O O
IPPROTO_IP IP_MULTICAST_TTL O O
IPPROTO_IP IP_MULTICAST_LOOP O O
IPPROTO_IP IP_MULTICAST_IF O O
IPPROTO_TCP TCP_KEEPALIVE O O
IPPROTO_TCP TCP_NODELAY O O
IPPROTO_TCP TCP_MAXSEG O O

8.3 緩衝區相關可選項

SO_SNDBUF & SO_RCVBUF

SO_SNDBUF

  • 輸出緩衝區相關的可選項。
  • 可用其讀取當前 I/O 大小,也可以更改緩衝區大小。

SO_RCVBUF

  • 輸入緩衝區相關的可選項。
  • 可用其讀取當前 I/O 大小,也可以更改緩衝區大小。

8.4 埠複用

主要用到 SO_REUSEADDR

先了解一些概念再介紹該選項。

8.4.1 time-wait 狀態

MSL:報文段最大生存時間,它是任何報文段被丟棄前在網路內的最長時間。

time-wait 狀態一般為 2 個MSL。其原因:

  • 保證 TCP 協議的全雙工連線能夠可靠關閉。
  • 保證這次連線的重複資料段從網路中消失。保證下次連線收到的資料包文段都是來自新連線的目標端。
  • 返回ACK最長為也給MSL,如果沒有到達對端,對端重發,到本端最大也要一個MSL,所以得2個MSL

若伺服器先異常斷開,四次揮手後進入 time-wait 狀態(一般為幾分鐘),在 time-wait 狀態時,該埠還是被佔用的。伺服器重啟後不能正常使用該埠,會輸出「bind() error」訊息。必須等待該埠被置為 close 狀態才能被正常使用。

但是有些情景下是不能接受的,若伺服器異常重啟,那得等待幾分鐘才能正常使用。
解決:使用SO_REUSEADDR來解決。

8.4.2 SO_REUSEADDR使用

在套接字的可選項中更改 SO_REUSEADDR 的狀態。
適當調整該引數,可將 Time-wait 狀態下的套接字埠號重新分配給新的套接字。
SO_REUSEADDR 的預設值為 0。
這就意味著無法分配 Time-wait 狀態下的套接字埠號。
因此需要將這個值改成 1 。

參考:

option = TRUE;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&option, sizeof(option));

8.4.3 SO_REUSEADDR 作用

SO_REUSEADDR 作用:

  1. 當有一個有相同本地地址和埠的socket1處於TIME_WAIT狀態時,而你啟動的程式的socket2要佔用該地址和埠,你的程式就要用到該選項。
  2. SO_REUSEADDR允許同一port上啟動同一伺服器的多個例項(多個程式)。但每個例項繫結的IP地址是不能相同的。在有多塊網路卡或用IP Alias技術的機器可以測試這種情況。
  3. SO_REUSEADDR允許單個程式繫結相同的埠到多個socket上,但每個socket繫結的ip地址不同。這和2很相似,區別請看UNPv1。
  4. SO_REUSEADDR允許完全相同的地址和埠的重複繫結。但這隻用於UDP的多播,不用於TCP。

8.5 Nagle 演算法

主要用到 TCP_NODELAY

Nagle 演算法

  • 應用於 TCP 層。
  • TCP 套接字預設使用 Nagle 演算法交換資料。
  • 演算法:只有接收到前一資料的 ACK 訊息, Nagle 演算法才傳送下一資料。

禁用

opt_val = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt_val, sizeof(opt_val));

檢視

getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&opt_val, sizeof(opt_val));

參考

相關文章