套接字的多種可選項(xiàng)
下列是針對(duì)SOL_SOCKET協(xié)議層的
可選項(xiàng) | 描述 |
---|---|
SO_REUSEADDR | 允許重用本地地址和端口,即使之前的連接處于 TIME_WAIT 狀態(tài)。 |
SO_KEEPALIVE | 啟用 TCP 連接的心跳檢測功能,保持連接活動(dòng)狀態(tài)。 |
SO_LINGER | 控制關(guān)閉連接時(shí)的行為。設(shè)置為 0 表示立即關(guān)閉連接,非零值則表示等待一段時(shí)間再關(guān)閉。 |
SO_RCVBUF | 控制套接字接收緩沖區(qū)的大小。 |
SO_SNDBUF | 控制套接字發(fā)送緩沖區(qū)的大小。 |
SO_RCVTIMEO | 設(shè)置套接字接收操作的超時(shí)時(shí)間。 |
SO_SNDTIMEO | 設(shè)置套接字發(fā)送操作的超時(shí)時(shí)間。 |
SO_BROADCAST | 允許廣播發(fā)送和接收。 |
SO_DEBUG | 啟用調(diào)試模式,在調(diào)試期間捕獲套接字的調(diào)試信息。 |
SO_ERROR | 獲取套接字錯(cuò)誤狀態(tài)。 |
SO_REUSEPORT | 允許多個(gè)套接字綁定到相同的地址和端口上。 |
SO_TYPE | 獲取套接字的類型。 |
下列是針對(duì)IPPROTO_TCP協(xié)議層的
可選項(xiàng) | 描述 |
---|---|
TCP_NODELAY | 禁用 Nagle 算法,允許小數(shù)據(jù)包立即發(fā)送。 |
TCP_MAXSEG | 設(shè)置 TCP 數(shù)據(jù)包的最大段長度(Maximum Segment Size,MSS)。 |
TCP_KEEPALIVE | 啟用 TCP 連接的心跳檢測功能,保持連接活動(dòng)狀態(tài)。 |
TCP_KEEPIDLE | 設(shè)置 TCP 連接的空閑時(shí)間閾值,超過閾值會(huì)發(fā)送心跳包。 |
TCP_KEEPINTVL | 設(shè)置 TCP 心跳包的發(fā)送間隔。 |
TCP_KEEPCNT | 設(shè)置 TCP 心跳包的發(fā)送次數(shù),達(dá)到次數(shù)仍無響應(yīng)則關(guān)閉連接。 |
TCP_LINGER2 | 控制關(guān)閉連接時(shí)的行為。設(shè)置為 0 表示立即關(guān)閉連接,非零值則表示等待一段時(shí)間再關(guān)閉。 |
TCP_SYNCNT | 設(shè)置 TCP 發(fā)送 SYN 數(shù)據(jù)包的最大次數(shù)。 |
下列是針對(duì)IPPROTO_IP協(xié)議層的
可選項(xiàng) | 描述 |
---|---|
IP_TTL | 設(shè)置 IP 頭部的生存時(shí)間(Time To Live),指定數(shù)據(jù)包在網(wǎng)絡(luò)中可以傳輸?shù)淖畲筇鴶?shù)。 |
IP_HDRINCL | 控制應(yīng)用程序是否提供自定義 IP 頭部。 |
IP_OPTIONS | 設(shè)置自定義 IP 選項(xiàng)。 |
IP_RECVOPTS | 允許應(yīng)用程序接收來自對(duì)方發(fā)送的 IP 選項(xiàng)。 |
IP_RETOPTS | 允許應(yīng)用程序發(fā)送自定義的返回路徑 IP 選項(xiàng)。 |
IP_PKTINFO | 允許應(yīng)用程序獲得接收到的數(shù)據(jù)包的相關(guān)信息,如源地址、目標(biāo)地址和接口索引等。 |
IP_RECVTOS | 允許應(yīng)用程序接收來自對(duì)方發(fā)送的服務(wù)類型(Type of Service)字段。 |
IP_RECVTTL | 允許應(yīng)用程序接收來自對(duì)方發(fā)送的生存時(shí)間(Time To Live)字段。 |
IP_MULTICAST_IF | 控制多播套接字的出站接口。 |
也許有些人看到上述表格會(huì)產(chǎn)生畏懼,但現(xiàn)在無需全部背下來或者理解。本章只會(huì)介紹少數(shù)幾種選項(xiàng)的設(shè)置。
getsockopt&setsockopt
可選項(xiàng)的設(shè)置可以依照下面兩個(gè)函數(shù)完成設(shè)置
下面函數(shù)是用于讀取套接字可選項(xiàng)
#include<sys/socket.h>
int getsockopt(int sock,int level,int optname,void *optval,socklen_t*optlen);
//成功時(shí)返回0,失敗時(shí)返回-1
sock //用于查看選項(xiàng)套接字文件描述符
level //查看可選項(xiàng)的協(xié)議層
optname //要查看的可選項(xiàng)名
optval //保存查看結(jié)果的緩沖地址值
optlen //該變量中保存通過第四個(gè)參數(shù)返回的可選信息的字節(jié)數(shù)
下面函數(shù)是用于設(shè)置可選項(xiàng)的函數(shù)
#include<sys/socket.h>
int setsockopt(int sock,int level,int optname,const void*optval,socklen_t optlen);
//成功時(shí)返回0,失敗時(shí)返回-1
sock //用于更改可選項(xiàng)的套接字文件描述符
level //用于更改可選項(xiàng)協(xié)議層
optname //要更改的可選項(xiàng)名
optval //用于保存更改的可選項(xiàng)信息的緩沖地址值
optlen //傳遞第四個(gè)參數(shù)的字節(jié)數(shù)
具體的演示放在下一小節(jié)里面展示
SO_SNDBUF&SO_RCVBUF
SO_RCVBUF是輸入緩沖大小相關(guān)可選項(xiàng),SO_SNDBUF是輸出緩沖大小相關(guān)可選項(xiàng)。用這兩個(gè)可選項(xiàng)即可讀取當(dāng)前IO緩沖大小,可以進(jìn)行更改。
下面的演示是獲取具體緩沖大小
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
void error_handling(char*message);
int main(int argc,char*argv[]){
int sock;
int snd_buf,rcv_buf,state;
socklen_t len;
sock=socket(PF_INET,SOCK_STREAM,0);
len=sizeof(snd_buf);
state=getsockopt(sock,SOL_SOCKET,SO_SNDBUF,(void*)&snd_buf,&len);
if(state)error_handling("getsockopt() error");
len=sizeof(rcv_buf);
state=getsockopt(sock,SOL_SOCKET,SO_RCVBUF,(void*)&rcv_buf,&len);
if(state)error_handling("getsockopt() error");
printf("Input buffer size: %d \n",rcv_buf);
printf("Output buffer size: %d \n",snd_buf);
return 0;
}
void error_handling(char*message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
下面的演示是修改具體緩沖大小
#include<"聲明與上一個(gè)示例相同,故省略">
void error_handling(char*message);
int main(int argc,char* argv[]){
int sock;
int snd_buf=1024*3,rcv_buf=1024*3;
int state;
socklen_t len;
sock=socket(PF_INET,SOCK_STREAM,0);
state=setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(void*)&rcv_buf,sizeof(rcv_buf));
if(state)error_handling("setsockopt() error");
state=setsockopt(sock,SOL_SOCKET,SO_SNDBUF,(void*)&snd_buf,sizeof(snd_buf));
if(state)error_handling("setsockopt() error");
len=sizeof(snd_buf);
state=getsockopt(sock,SOL_SOCKET,SO_SNDBUF,(void*)&snd_buf,&len);
if(state)error_handling("getsockopt() error");
len=sizeof(rcv_buf);
state=getsockopt(sock,SOL_SOCKET,SO_RCVBUF,(void*)&rcv_buf,&len);
if(state)error_handling("getsockopt() error");
printf("Input buffer size: %d \n",rcv_buf);
printf("Output buffer size: %d \n",snd_buf);
return 0;
}
void error_handling(char*message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
最后輸出的結(jié)果可能會(huì)和我們設(shè)置的大小完全不同。因?yàn)樵倬彌_大小上需要謹(jǐn)慎處理,因此不會(huì)100%的按照我夢的請(qǐng)求設(shè)置緩沖大小,但也大致反映出了通過setsockopt函數(shù)設(shè)置緩沖大小。
SO_REUSEADDR
先描述一下前面章節(jié)的代碼情景,讓客戶端先通知服務(wù)器端終止程序。在客戶端控制臺(tái)輸入Q消息時(shí)調(diào)用close函數(shù)(參考第4章),向服務(wù)器端發(fā)送FIN消息并經(jīng)過四次握手過程。當(dāng)然,輸入CTRL+C時(shí)也會(huì)向服務(wù)器傳遞FIN消息。強(qiáng)制終止程序時(shí),由操作系統(tǒng)關(guān)閉文件及套接字,此過程相當(dāng)于調(diào)用close函數(shù),也會(huì)向服務(wù)器端傳遞FIN消息?!暗床坏绞裁刺厥猬F(xiàn)象???”
是的,通常都是由客戶端先請(qǐng)求斷開連接,所以不會(huì)發(fā)生特別的事情。重新運(yùn)行服務(wù)器端也
不成問題,但按照如下方式終止程序時(shí)則不同。
“服務(wù)器端和客戶端已建立連接的狀態(tài)下,向服務(wù)器端控制臺(tái)輸入CTRL+C,即強(qiáng)
制關(guān)閉服務(wù)器端?!?br> 這主要模擬了服務(wù)器端向客戶端發(fā)送FIN消息的情景。但如果以這種方式終止程序,那服務(wù)器端重新運(yùn)行時(shí)將產(chǎn)生問題。如果用同一端口號(hào)重新運(yùn)行服務(wù)器端,將輸出“bind() error”消息,并且無法再次運(yùn)行。但在這種情況下,再過大約3分鐘即可重新運(yùn)行服務(wù)器端。
上述2種運(yùn)行方式唯一的區(qū)別就是誰先傳輸FIN消息,但結(jié)果卻迥然不同,原因何在呢?
Time_wait狀態(tài)
相信各位已經(jīng)對(duì)四次握手有了很好的理解,現(xiàn)在再來描述一下上述過程。假設(shè)主機(jī)A是服務(wù)器端,因?yàn)槭侵鳈C(jī)A向B發(fā)送FIN消息,故可以想象成服務(wù)器端在控制臺(tái)輸入CTRL+C。但問題是套接字經(jīng)過四次握手過程后并非立即消除,而是要經(jīng)過一段時(shí)間的Time-wait狀態(tài)。當(dāng)然,只有先斷開連接的(先發(fā)送FIN消息的)主機(jī)才經(jīng)過Time-wait狀態(tài)。因此,若服務(wù)器端先斷開連接,則無法立即重新運(yùn)行。套接字處在Time-wait過程時(shí),相應(yīng)端口是下在使用的狀態(tài)。因此,就像之前驗(yàn)證過的,bind函數(shù)調(diào)用過程中當(dāng)然會(huì)發(fā)生錯(cuò)誤。到底為什么會(huì)有Time-wait狀態(tài)呢?
假設(shè)主機(jī)A向主機(jī)B傳輸ACK消息后立即消除套接字。但最后這條ACK消息在傳遞途中丟失,未能傳給主機(jī)B。這時(shí)會(huì)發(fā)生什么?主機(jī)B會(huì)認(rèn)為之前自己發(fā)送的FIN消息未能抵達(dá)主機(jī)A,維而試圖重傳。但此時(shí)主機(jī)A已是完全終止的狀態(tài),因此主機(jī)B永遠(yuǎn)無法收到從主機(jī)A最后傳來的ACK消息。相反,若主機(jī)A的套接字處在Time-wait狀態(tài),則會(huì)向主機(jī)B重傳最后的ACK消息,主機(jī)B也可以正常終止。基于這些考慮,先傳輸FIN消息的主句應(yīng)經(jīng)過Time-wait過程。
地址再分配
Time-wait看似重要,但并不一定討人喜歡??紤]一下系統(tǒng)發(fā)生故障從而緊急停止的情況。這時(shí)需要盡快重啟服務(wù)器端以提供服務(wù),但因處于Time-wait狀態(tài)而必須等待幾分鐘。因此,Time-wait并非只有優(yōu)點(diǎn),而且有些情況下可能引發(fā)更大問題。
在主機(jī)A的四次握手過程中,如果最后的數(shù)據(jù)丟失,則主機(jī)B會(huì)認(rèn)為主機(jī)A未能收到自己發(fā)送的FIN消息,因此重傳。這時(shí),收到FIN消息的主機(jī)A將重啟Time-wait計(jì)時(shí)器。因此,如果網(wǎng)絡(luò)狀況不理想,Time-wait狀態(tài)將持續(xù)。
解決方案就是在套接字的可選項(xiàng)中更改SO_REUSEADDR的狀態(tài)。適當(dāng)調(diào)整該參數(shù),可將Time-wait狀態(tài)下的套接字端口號(hào)重新分配給新的套接字。SO_REUSEADDR的默認(rèn)值為0(假),這就意味著無法分配Time-wait狀態(tài)下的套接字端口號(hào)。因此需要將這個(gè)值改成1(真)。
只需要添加入如下代碼即可
optlen=sizeof(option);
option=TRUE;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR,(void*)&option,optlen);
基于Windows的實(shí)現(xiàn)
套接字可選項(xiàng)及其相關(guān)內(nèi)容與操作系統(tǒng)無關(guān),特別是本章的可選項(xiàng),因此在Windows平臺(tái)和Linux平臺(tái)下并無區(qū)別。接下來介紹讀取和設(shè)定的兩個(gè)函數(shù)。
#include <winsock2.h>
int getsockopt(SOCKET sock, int level, int optname, char * optval, int * optlen);
//成功時(shí)返回0,失敗時(shí)返回SOCKET_ERROR。
sock //要查看可選項(xiàng)的套接字句柄。
level //要查看的可選項(xiàng)協(xié)議層。
optname //要查看的可選項(xiàng)名。
optval //保存查看結(jié)果的緩沖地址值。
optlen //向第四個(gè)參數(shù)optval傳遞的緩沖大小。調(diào)用結(jié)束后,該變量中保存通過第四個(gè)參數(shù)返回的可
//選項(xiàng)字節(jié)數(shù)。
可以看到,除了optval類型變成char指針外,與Linux中的getsockopt函數(shù)相比并無太大區(qū)別(Linux中是void型指針)。將Linux中的示例移植到Windows時(shí),應(yīng)做適當(dāng)?shù)念愋娃D(zhuǎn)換。接下來給出setsockopt函數(shù)。文章來源:http://www.zghlxwxcb.cn/news/detail-597049.html
#include <winsock2.h>
int setsockopt(SOCKET sock, int level, int optname, const char* optval, int
optlen);
//成功時(shí)返回0,失敗時(shí)返回SOCKET_ERROR。
sock //要更改可選項(xiàng)的套接字句柄。
level //要更改的可選項(xiàng)協(xié)議層。
optname //要更改的可選項(xiàng)名。
optval //保存要更改的可選項(xiàng)信息的緩沖地址值。
optlen //傳入第四個(gè)參數(shù)optval的可選項(xiàng)信息的字節(jié)數(shù)。
setsockopt函數(shù)也與Linux版的毫無二致。文章來源地址http://www.zghlxwxcb.cn/news/detail-597049.html
到了這里,關(guān)于TCP/IP網(wǎng)絡(luò)編程 第九章:套接字的多種可選項(xiàng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!