目錄
前言
說明:
- 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 阿婆主