Linux下getsockopt/setsockopt 函數說明
【 getsockopt/setsockopt系統調用】 功能描述: 擷取或者設定與某個通訊端關聯的選 項。選項可能存在於多層協議中,它們總會出現在最上面的通訊端層。當操作通訊端選項時,選項位於的層和選項的名稱必須給出。為了操作通訊端層的選項,應該 將層的值指定為SOL_SOCKET。為了操作其它層的選項,控制選項的合適協議號必須給出。例如,為了表示一個選項由TCP協議解析,層應該設定為協議 號TCP。 用法: #include <sys/types.h> #include <sys/socket.h>
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen); 參數: sock:將要被設定或者擷取選項的通訊端。 level:選項所在的協議層。 optname:需要訪問的選項名。 optval:對於getsockopt(),指向返回選項值的緩衝。對於setsockopt(),指向包含新選項值的緩衝。 optlen:對於getsockopt(),作為入口參數時,選項值的最大長度。作為出口參數時,選項值的實際長度。對於setsockopt(),現選項的長度。
返回說明: 成功執行時,返回0。失敗返回-1,errno被設為以下的某個值 EBADF:sock不是有效檔案描述詞 EFAULT:optval指向的記憶體並非有效進程空間 EINVAL:在調用setsockopt()時,optlen無效 ENOPROTOOPT:指定的協議層不能識別選項 ENOTSOCK:sock描述的不是通訊端 |
首Crowdsourced Security Testing道setsockopt()函數的KEEPALIVE屬性是周期地測試連接是否仍存活,
我上網查了很多資料還是不知道如何使用,
最後硬著頭皮自己寫了一個伺服器端和一個用戶端的通訊端串連
分別設定了兩端的KEEPALIVE屬性為開啟
伺服器端:
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<error.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<unistd.h>
#define SERVPORT 6000 //設定伺服器服務連接埠為6000
#define MAX_LISTEN_SOCK_NUM 20 //設定可監聽通訊端的最大個數為20
int main()
{
//sockfd為本地監聽通訊端標識符,client_fd為用戶端通訊端標識符
int sockfd,client_fd;
struct sockaddr_in my_addr;
struct sockaddr_in client_addr;
//建立本地監聽通訊端
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
perror("通訊端建立失敗!/n");
exit(1);
}
//設定通訊端的屬性使它能夠在電腦重啟的時候可以再次使用通訊端的連接埠和IP
int err,sock_reuse=1;
err=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(char *)&sock_reuse,sizeof(sock_reuse));
if(err!=0){
printf("通訊端可重用設定失敗!/n");
exit(1);
}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(SERVPORT);
my_addr.sin_addr.s_addr=INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
//綁定通訊端
if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1){
perror("綁定失敗!/n");
exit(1);
}
//設定監聽
if((listen(sockfd,MAX_LISTEN_SOCK_NUM))==-1){
perror("設定監聽失敗!/n");
exit(1);
}
printf("通訊端進入監聽狀態,等待請求串連:/n");
//int time_to_quit=1;
//while(time_to_quit){ //可以通過設定time_to_quit來主動關閉伺服器端
while(1){
//有串連請求時進行串連
socklen_t sin_size=sizeof(struct sockaddr_in);
if((client_fd=accept(sockfd,(struct sockaddr *)&client_addr,&sin_size))==-1){
perror("接受串連失敗!/n");
continue;
}
int opt;
socklen_t len=sizeof(int);
if((getsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(char*)&opt,&len))==0){
printf("SO_KEEPALIVE Value: %d/n", opt);
}
printf("接到一個來自%s的串連/n",inet_ntoa(client_addr.sin_addr));
//建立子進程來處理已串連的用戶端通訊端
if(send(client_fd,"您好,您已經串連成功!/n",50,0)==-1){
perror("發送通知資訊失敗!/n");
exit(0);
}
}
close(client_fd);
return 0;
}
用戶端:
#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#define MAXDATASIZE 100 /*每次最大資料轉送量*/
int main()
{
int sockfd,nbytes,serv_port;
char buf_serv_ip[16],buf[26];
struct sockaddr_in serv_addr;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
perror("建立通訊端失敗!/n");
exit(1);
}
//建立通訊端成功後設定其可重用的屬性
int KeepAlive=1;
socklen_t KPlen=sizeof(int);
if(setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(char *)&KeepAlive,KPlen)!=0){
perror("設定周期測試連接是否仍存活失敗!/n");
exit(1);
}
printf("請輸入要串連主機的IP地址:/n");
scanf("%s",buf_serv_ip);
printf("請輸入要串連主機的連接埠號碼:/n");
scanf("%d",&serv_port);
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(buf_serv_ip);
serv_addr.sin_port=htons(serv_port);
bzero(&(serv_addr.sin_zero),8);
if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
perror("串連伺服器失敗!/n");
exit(1);
}
printf("串連伺服器成功!/n");
//在此處可以先接受判斷將要接受資料的長度再建立數組
if((nbytes=recv(sockfd,buf,26,0))==-1){
perror("接受資料失敗!/n");
exit(1);
}
buf[nbytes]='/0';
printf("接受的資料為:%s/n",buf);
close(sockfd);
return 0;
}
分別在兩個終端運(在同一個主機上)行:先啟動伺服器端;再啟動用戶端,向伺服器端請求串連,串連成功後,用戶端通訊端關閉,伺服器端通訊端始終開啟,等待兩個小時多也沒有反應,
肯請高手指點:
(1)這樣做能達到預期報告串連是否仍存在的效果嗎,
(2)不能的話應該如何做,
(3)還有getsockopt()的相關功能又是什麼哪。在上面的程式中沒有用到該函數
(4)看了unix網路編程上的一點關於該問題的解釋還是不明白,因為她提到要兩個小時才回應,但等了撒小時也沒有反應,她還說可以縮短時間,我卻不知道如何縮短 getsockopt和setsockopt
int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen)
int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen)
level指定控制通訊端的層次.可以取三種值:
1)SOL_SOCKET:通用通訊端選項.
2)IPPROTO_IP:IP選項.
3)IPPROTO_TCP:TCP選項.
optname指定控制的方式(選項的名稱),我們下面詳細解釋
optval獲得或者是設定通訊端選項.根據選項名稱的資料類型進行轉換
選項名稱 說明 資料類型
========================================================================
SOL_SOCKET
------------------------------------------------------------------------
SO_BROADCAST 允許發送廣播資料 int
SO_DEBUG 允許調試 int
SO_DONTROUTE 不尋找路由 int
SO_ERROR 獲得通訊端錯誤 int
SO_KEEPALIVE 保持串連 int
SO_LINGER 延遲關閉串連 struct linger
SO_OOBINLINE 帶外資料放入正常資料流 int
SO_RCVBUF 接收緩衝區大小 int
SO_SNDBUF 發送緩衝區大小 int
SO_RCVLOWAT 接收緩衝區下限 int
SO_SNDLOWAT 發送緩衝區下限 int
SO_RCVTIMEO 接收逾時 struct timeval
SO_SNDTIMEO 發送逾時 struct timeval
SO_REUSERADDR 允許重用本地地址和連接埠 int
SO_TYPE 獲得通訊端類型 int
SO_BSDCOMPAT 與BSD系統相容 int
========================================================================
IPPROTO_IP
------------------------------------------------------------------------
IP_HDRINCL 在資料包中包含IP首部 int
IP_OPTINOS IP首部選項 int
IP_TOS 服務類型
IP_TTL 存留時間 int
========================================================================
IPPRO_TCP
------------------------------------------------------------------------
TCP_MAXSEG TCP最大資料區段的大小 int
TCP_NODELAY 不使用Nagle演算法 int
========================================================================
返回說明:
成功執行時,返回0。失敗返回-1,errno被設為以下的某個值
EBADF:sock不是有效檔案描述詞
EFAULT:optval指向的記憶體並非有效進程空間
EINVAL:在調用setsockopt()時,optlen無效
ENOPROTOOPT:指定的協議層不能識別選項
ENOTSOCK:sock描述的不是通訊端
SO_RCVBUF和SO_SNDBUF每個套介面都有一個發送緩衝區和一個接收緩衝區,使用這兩個套介面選項可以改變預設緩衝區大小。
// 接收緩衝區
int nRecvBuf=32*1024; //設定為32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//發送緩衝區
int nSendBuf=32*1024;//設定為32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
注意:
當設定TCP套介面接收緩衝區的大小時,函數調用順序是很重要的,因為TCP的視窗規模選項是在建立串連時用SYN與對方互換得到的。對於客戶,SO_RCVBUF選項必須在connect之前設定;對於伺服器,SO_RCVBUF選項必須在listen前設定。