目錄
前言
說明:
- 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
作用:
- 當有一個有相同本地地址和埠的socket1處於TIME_WAIT狀態時,而你啟動的程式的socket2要佔用該地址和埠,你的程式就要用到該選項。
- SO_REUSEADDR允許同一port上啟動同一伺服器的多個例項(多個程式)。但每個例項繫結的IP地址是不能相同的。在有多塊網路卡或用IP Alias技術的機器可以測試這種情況。
- SO_REUSEADDR允許單個程式繫結相同的埠到多個socket上,但每個socket繫結的ip地址不同。這和2很相似,區別請看UNPv1。
- 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));
參考
- 《TCP/IP網路程式設計》
- 李柱明部落格-TCP/IP TCP詳細筆記
- github 阿婆主