Linux网络编程之套接字选项设置 .

1.介绍

在Linux网络编程中,有时需要设置地址复用,允许发送广播包,将主机加入某个多播组,设置发送与接收缓冲区的大小,设置发送与接收的超时时间,将套接字绑定到某个接口上,发送TCP探测包查看客户端是否保持连接等,这些都需要对套接字选项进行设置.而对套接字选项进行操作的主要有以下两个函数,setsockopt与getsockopt. 这两个函数不仅能够操作套接字层,而且能够操作IP层与TCP层.

2.相关函数

#include <sys/types.h>

#include <sys/socket.h>

int getsockopt(int s,int level,int optname,void*optval,socklen_t *optlen);

参数:

s-套接字描述符

level-选项所在的层,主要有SOL_SOCKET(套接字层),IPPROTO_IP(IP选项),IPPROTO_TCP(TCP选项).

optname-选项名

optval-所操作的缓冲区,即参数缓冲区

optlen-传入参数的最大长度的指针(返回参数的实际长度)

返回值:

函数执行成功返回0,失败返回-1.

int setsockopt(int s,int level,int optname,const void*optval,socklen_t optlen);

参数:

s-套接字描述符

level-套接字所在的层

optname-套接字选项名

optval-所操作的缓冲区指针

optlen-所传入参数的实际长度

返回值:

函数执行成功返回0,失败返回-1.


套接字的一些选项值如下表所示:

levelOptnamegetset说明标志数据类型
       
SOL_SOCKETSO_BROADCASTyy允许发送广播数据报yint
 SO_DEBUGyy使能调试跟踪yint
 SO_DONTROUTEyy旁路路由表查询yint
 SO_ERRORy 获取待处理错误并消除 int
 SO_KEEPALIVEyy周期性测试连接是否存活yint
 SO_LINGERyy若有数据待发送则延迟关闭 linger{}
 SO_OOBINLINEyy让接收到的带外数据继续在线存放yint
 SO_RCVBUFyy接收缓冲区大小 int
 SO_SNDBUFyy发送缓冲区大小 int
 SO_RCVLOWATyy接收缓冲区低潮限度 int
 SO_SNDLOWATyy发送缓冲区低潮限度 int
 SO_RCVTIMEOyy接收超时 timeval{}
 SO_SNDTIMEOyy发送超时 timeval{}
 SO_REUSEADDRyy允许重用本地地址yint
 SO_REUSEPORTyy允许重用本地地址yint
 SO_TYPEy 取得套接口类型 int
 SO_USELOOPBACKyy路由套接口取得所发送数据的拷贝yint
       
IPPROTO_IPIP_HDRINCLyyIP头部包括数据yint
 IP_OPTIONSyyIP头部选项 见后面说明
 IP_RECVDSTADDRyy返回目的IP地址yint
 IP_RECVIFyy返回接收到的接口索引yint
 IP_TOSyy服务类型和优先权 int
 IP_TTLyy存活时间 int
 IP_MULTICAST_IFyy指定外出接口 in_addr{}
 IP_MULTICAST_TTLyy指定外出TTL u_char
 IP_MULTICAST_LOOPyy指定是否回馈 u_char
 IP_ADD_MEMBERSHIP y加入多播组 ip_mreq{}
 IP_DROP_MEMBERSHIP y离开多播组 ip_mreq{}
       
IPPROTO_ICMPV6ICMP6_FILTERyy指定传递的ICMPv6消息类型 icmp6_filter{}
       
IPPROTO_IPV6IPV6_ADDRFORMyy改变套接口的地址结构 int
 IPV6_CHECKSUMyy原始套接口的校验和字段偏移 int
 IPV6_DSTOPTSyy接收目标选项yint
 IPV6_HOPLIMITyy接收单播跳限yint
 IPV6_HOPOPTSyy接收步跳选项yint
 IPV6_NEXTHOPyy指定下一跳地址ysockaddr{}
 IPV6_PKTINFOyy接收分组信息yint
 IPV6_PKTOPTIONSyy指定分组选项 见后面说明
 IPV6_RTHDRyy接收原路径yint
 IPV6_UNICAST_HOPSyy缺省单播跳限 int
 IPV6_MULTICAST_IFyy指定外出接口 in6_addr{}
 IPV6_MULTICAST_HOPSyy指定外出跳限 u_int
 IPV6_MULTICAST_LOOPyy指定是否回馈yu_int
 IPV6_ADD_MEMBERSHIP y加入多播组 ipv6_mreq{}
 IPV6_DROP_MEMBERSHIP y离开多播组 ipv6_mreq{}
       
IPPROTO_TCPTCP_KEEPALIVEyy控测对方是否存活前连接闲置秒数 int
 TCP_MAXRTyyTCP最大重传时间 int
 TCP_MAXSEGyyTCP最大分节大小 int
 TCP_NODELAYyy禁止Nagle算法yint
 TCP_STDURGyy紧急指针的解释yint


3.套接字选项设置实例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <unistd.h>
#include <netinet/tcp.h>

//获取与设置套接字选项的函数
/**
int getsockopt(int s,int level,int optname,void*optval,socklen_t *optlen);
int setsockopt(int s,int level,int optname,const void*optval,socklen_t optlen);
s:表示要获取或者是设置的套接字描述符
level:协议所在的层
optname:选项名
optval:getsockopt指向用于保存返回选项值的缓冲区,setsockopt指向设置参数缓冲区
optlen:getsockopt用于传入参数时表示optval的最大长度,传出参数时表示optval的实际长度。对于setsockopt表示optval的实际长度
**/
//定认一个共用体用于保存结果,由于返回值具有不同的类型所以必须用共用体
typedef union optval{
   int val;
   struct linger linger;
   struct timeval tv;
   unsigned char str[16];
   

}val;
static val optval;
//数据类型的定义
typedef enum valtype{
  VALINT,//int 类型
  VALLINGER,//struct linger类型
  VALTIMEVAL,//struct timeval类型
  VALUCHAR,//字符串
  VALMAX,//错误类型
}valtype;
//保存套接字选项的结构
typedef struct sopts{
  int level;//套接字选项级别
  int optname;//套接字选项名称
  char*name;//套接字名称
  valtype valtype;//套接字返回参数类型

}sopts;
//套接字数组
sopts sockopts[]={
  {SOL_SOCKET,SO_BROADCAST,"SO_BROADCAST",VALINT},//是否支持广播
  {SOL_SOCKET,SO_DEBUG,"SO_DEBUG",VALINT}, //是否支持TCP调试
  {SOL_SOCKET,SO_DONTROUTE,"SO_DONTROUTE",VALINT},//是否需要路由
  {SOL_SOCKET,SO_ERROR,"SO_ERROR",VALINT},//返回错误信息
  {SOL_SOCKET,SO_KEEPALIVE,"SO_KEEPALIVE",VALINT},//是否支持自动探测TCP连接
  {SOL_SOCKET,SO_LINGER,"SO_LINGER",VALLINGER},//是否支持TCP关闭模式
  {SOL_SOCKET,SO_OOBINLINE,"SO_OOBINLINE",VALINT},//是否支持带外数据与普通数据一起发送
  {SOL_SOCKET,SO_RCVBUF,"SO_RECVBUF",VALINT},//设置接收缓冲区大小
  {SOL_SOCKET,SO_SNDBUF,"SO_SNDBUF",VALINT},//设置发送缓冲区大小
  {SOL_SOCKET,SO_SNDTIMEO,"SO_SNDTIMEO",VALTIMEVAL},//设置发送数据的超时时间
  {SOL_SOCKET,SO_RCVTIMEO,"SO_RCVTIMEO",VALTIMEVAL},//设置接收数据的超时时间
  {SOL_SOCKET,SO_RCVLOWAT,"SO_RCVLOWAT",VALINT},//设置接收缓冲区的最小字节数
  {SOL_SOCKET,SO_SNDLOWAT,"SO_SNDLOWAT",VALINT},//设置发送缓冲区的最小字节数
  {SOL_SOCKET,SO_REUSEADDR,"SO_REUSERADDR",VALINT},//是否允许端口重用
  {SOL_SOCKET,SO_TYPE,"SO_TYPE",VALINT},//套接字类型
  {SOL_SOCKET,SO_BINDTODEVICE,"SO_BINDTODEVICE",VALUCHAR},//是否将套接字绑定到指定端口
  {SOL_SOCKET,SO_PRIORITY,"SO_PRIORITY",VALINT},//设置套接字的优先级别
  {IPPROTO_IP,IP_HDRINCL,"IP_HDRINCL",VALINT},//用户是否自己填写IP头部
  {IPPROTO_IP,IP_MULTICAST_TTL,"IP_MULTICAST_TTL",VALUCHAR},//多播的最大跳数
  {0,0,NULL,VALMAX}

};
static void display(sopts *sockopt,int len,int err);
//获得套接字选项值
int main(int argc,char*argv[]){
 int err=-1;
 int len=0;
 int i=0;
 int s=socket(AF_INET,SOCK_STREAM,0);
//首先设置套接字选项信息,然后再返回套接字选项,先用setsockopt,再用getsockopt
//设置广播选项
int broadcast=1;
err=setsockopt(s,SOL_SOCKET,SO_BROADCAST,&broadcast,sizeof(broadcast));//broadcast指针指向缓冲区,缓冲区存储的是允许广播选项的值,值的长度为&len
if(err){
  perror("setsockopt error");
}
//设置可调试选项
int debug=1;//内核程序跟踪此套接字上的接收与发送数据,并将数据放到一个环形缓冲区,通过trpt查看,仅支持TCP
err=setsockopt(s,SOL_SOCKET,SO_DEBUG,&debug,sizeof(debug));
if(err){
   printf("debug error\n");
}
//SO_KEEPALIVE用于探测TCP连接是否保持,主要使用在长时间没有数据响应的TCP连接,如TELNET客户端长时间不使用,这需要服务器去探测对方是否活动
int keepalive=10;
err=setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,&keepalive,sizeof(keepalive));
if(err){
  printf("keepalive error");
}
//SO_LINGER选项
/**
主要来设置TCP连接关闭时,用户缓冲区怎么处理
struct linger{
int l_onoff; 是否设置延迟关闭
int l_linger;  超时时间
}
如果l_onoff=0,表示没有使用系统默认的close套接字行为,close函数会立即返回,在可能的情况下尽量发送缓冲区内的数据,但是用户没有办法知道缓冲区内的数据是否已经完全被发送,即不确定性
如果l_onoff=1,l_linger表示关闭连接的超时时间,l_linger为非0时,表示超时的秒数,会在超时之前尽量发送数据,检查close函数的返回值,如果发送成功,close返回0,否则缓冲区内的任何数据将会被丢失,返回EWOULDBLOCK.
如果l_onoff=1,l_linger=0,表示立刻关闭,缓冲区内的数据将会丢失。
**/
//设置超时间为60秒
struct linger l;
l.l_onoff=1;//延迟关闭
l.l_linger=60;
err=setsockopt(s,SOL_SOCKET,SO_LINGER,&l,sizeof(l));
if(err){
  perror("SO_LINGER ERROR");
}
//带外数据指的是那些紧急的数据,TCP优先发送带外数据,这些数据使用特殊的通道来接收。如果采用SO_OOBINLINE表示在普通数据流中接收带外数
int oobinline=1;
err=setsockopt(s,SOL_SOCKET,SO_OOBINLINE,&oobinline,sizeof(oobinline));
if(err){
  perror("SO_OOBINLINE ERROR");
}
//设置发送缓冲区与接收缓冲区的大小SO_RCVBUF与SO_SNDBUF,将发送缓冲区的大小设置为8192字节,将接收缓冲区的大小设置为16384字节
//TCP具有流量控制的功能,不允许发送比滑动窗口大的数据,而UDP没有流量控制的功能,当发送的数据比接收缓冲区多时,会丢失数据,即快的发送者会淹没慢的接收者
int sndsize=8192;//真实的缓冲区大小为用户输入的两倍
int rcvsize=16384;
err=setsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndsize,sizeof(sndsize));
if(err){
  perror("SO_SNDBUF ERROR");
}
err=setsockopt(s,SOL_SOCKET,SO_RCVBUF,&rcvsize,sizeof(rcvsize));
if(err){
  perror("SO_RCVBUF ERROR");
}
//发送超时时延与接收超时时延SO_RCVTIMEO,SO_SNDTIMEO默认情况下recv函数没有接收到数据会一直阻塞的
struct timeval t;
t.tv_sec=10;//设置为10秒
t.tv_usec=100;
int len1=sizeof(t);
err=setsockopt(s,SOL_SOCKET,SO_SNDTIMEO,&t,len1);
if(err){
  perror("SO_SNDTIMEO error");
}
err=setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,&t,len1);
if(err){
  perror("SO_RCVTIMEO");
}
//SO_RCVLOWAT接收缓存有最小数据字节数,读才不会阻塞。SO_SNDLOWAT为发送缓冲区的最小值,默认情况下都为1字节,写才不会阻塞
//将发送缓冲区空闲空间的下限值设定为2048字节,将接收缓冲区的空闲空间设置为1字节
int rcvlowat=10;
int sndlowat=10;//发送缓冲区的最低空闲字节数不能设置
err=setsockopt(s,SOL_SOCKET,SO_RCVLOWAT,&rcvlowat,sizeof(rcvlowat));
if(err){
  perror("SO_RCVLOWAT ERROR");
}
err=setsockopt(s,SOL_SOCKET,SO_SNDLOWAT,&sndlowat,sizeof(sndlowat));
if(err){
  perror("SO_SNDLOWAT ERROR");
}
//SO_REUSERADDR表示允许重复使用本地端口,某些非正常退出的服务器程序会占用端口一段时间才释放,这时将不使用SO_REUSERADDR将不能绑定到端口
int reuseaddr=1;//开启端口重复使用功能
err=setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&reuseaddr,sizeof(reuseaddr));
if(err){
  perror("SO_REUSEADDR ERROR");
}

//SO_BINDTODEVICE,可以将套接字与某个接口相绑定
char ifname[]="eth0";//sizeof表示空间大小为5,加\0,strlen遇到\0结束为4
err=setsockopt(s,SOL_SOCKET,SO_BINDTODEVICE,ifname,5);//数组名可以表示地址
if(err){
  perror("SO_BINDTODEVICE ERROR");
}
//SO_PRIORITY对套接字优先权的设置,高优先权的数据被优处理,优先权范围0-6
int priority=6;
err=setsockopt(s,SOL_SOCKET,SO_PRIORITY,&priority,sizeof(priority));
if(err){
  perror("SO_PRIORITY ERROR");

}
/**
SOL_SOCKET:对套接字级别进行操作
IPPROTO_IP:对IP层协议进行操作
IPPROTO_TCP:对TCP层协议进行操作
**/
 while(sockopts[i].valtype!=VALMAX){
   len=sizeof(optval);//getsockopt传入时的最大长度
   err=getsockopt(s,sockopts[i].level,sockopts[i].optname,&optval,&len);//返回的值保存在optval缓冲区中,传出值表示返回选项值的实际长度
 //第一个参数是套接字描述符
//第二个参数是选项所在的协议层
//第三个参数是选项名称
//第四个参数是返回值的缓冲区
//第五个参数是返回值缓冲区的长度
   display(&sockopts[i],len,err);
   i++;
}

close(s);
return 0;

}
static void display(sopts *sockopt,int len,int err){
  if(err==-1){
   printf("optname %s not support\n",sockopt->name);
   return;
}
 
switch(sockopt->valtype){
 case VALINT:
    printf("optname %s:is %d\n",sockopt->name,optval.val);//整形值
break;
case VALLINGER:
    printf("optname %s:is %d(ON/OFF),%d to linger\n",sockopt->name,optval.linger.l_onoff,optval.linger.l_linger);//linger打开,延迟时间
break;
case VALTIMEVAL:
    printf("optname %s:default is %.06f\n",sockopt->name,((((double)optval.tv.tv_sec*1000000+(double)optval.tv.tv_usec))/(double)1000000));
break;
case VALUCHAR:
 {
    int i=0;
        
        printf("optname %s:default is",sockopt->name);
        for(i=0;i<len;i++){
        printf("%02x ",optval.str[i]);

    }
    printf("\n");
 }
break;
default:
break;
}
}

运行结果:
[root@localhost ~]# ./socket-opt
SO_SNDLOWAT ERROR: Protocol not available
optname SO_BROADCAST:is 1
optname SO_DEBUG:is 1
optname SO_DONTROUTE:is 0
optname SO_ERROR:is 0
optname SO_KEEPALIVE:is 1
optname SO_LINGER:is 1(ON/OFF),60 to linger
optname SO_OOBINLINE:is 1
optname SO_RECVBUF:is 32768
optname SO_SNDBUF:is 16384
optname SO_SNDTIMEO:is 10.001000
optname SO_RCVTIMEO:is 10.001000
optname SO_RCVLOWAT:is 10
optname SO_SNDLOWAT:is 1
optname SO_REUSERADDR:is 1
optname SO_TYPE:is 1
optname SO_BINDTODEVICE not support
optname SO_PRIORITY:is 6
optname IP_HDRINCL:is 0
optname IP_MULTICAST_TTL:is01 00 00 00


总结:

本文主要介绍了套接字选项设置的相关函数,以及套接字层(SOL_SOCKET),IP层(IPPROTO_IP)与TCPP层(IPPROTO_TCP)选项的含义,最后给出一个具体的例子来设置和得到套接字选项的值. 套接字选项的设置在广播和多播以及原始套接字直接得到IP包等方面有着重要的应用.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值