linux网络编程api,Linux网络编程之套接字选项设置

1.介绍

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

这两个函数不仅能够操作套接字层,而且能够操作IP层与TCP层.

2.相关函数

#include

#include

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.

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

level

Optname

get

set

说明

标志

数据类型

SOL_SOCKET

SO_BROADCAST

y

y

允许发送广播数据报

y

int

SO_DEBUG

y

y

使能调试跟踪

y

int

SO_DONTROUTE

y

y

旁路路由表查询

y

int

SO_ERROR

y

获取待处理错误并消除

int

SO_KEEPALIVE

y

y

周期性测试连接是否存活

y

int

SO_LINGER

y

y

若有数据待发送则延迟关闭

linger{}

SO_OOBINLINE

y

y

让接收到的带外数据继续在线存放

y

int

SO_RCVBUF

y

y

接收缓冲区大小

int

SO_SNDBUF

y

y

发送缓冲区大小

int

SO_RCVLOWAT

y

y

接收缓冲区低潮限度

int

SO_SNDLOWAT

y

y

发送缓冲区低潮限度

int

SO_RCVTIMEO

y

y

接收超时

timeval{}

SO_SNDTIMEO

y

y

发送超时

timeval{}

SO_REUSEADDR

y

y

允许重用本地地址

y

int

SO_REUSEPORT

y

y

允许重用本地地址

y

int

SO_TYPE

y

取得套接口类型

int

SO_USELOOPBACK

y

y

路由套接口取得所发送数据的拷贝

y

int

IPPROTO_IP

IP_HDRINCL

y

y

IP头部包括数据

y

int

IP_OPTIONS

y

y

IP头部选项

见后面说明

IP_RECVDSTADDR

y

y

返回目的IP地址

y

int

IP_RECVIF

y

y

返回接收到的接口索引

y

int

IP_TOS

y

y

服务类型和优先权

int

IP_TTL

y

y

存活时间

int

IP_MULTICAST_IF

y

y

指定外出接口

in_addr{}

IP_MULTICAST_TTL

y

y

指定外出TTL

u_char

IP_MULTICAST_LOOP

y

y

指定是否回馈

u_char

IP_ADD_MEMBERSHIP

y

加入多播组

ip_mreq{}

IP_DROP_MEMBERSHIP

y

离开多播组

ip_mreq{}

IPPROTO_ICMPV6

ICMP6_FILTER

y

y

指定传递的ICMPv6消息类型

icmp6_filter{}

IPPROTO_IPV6

IPV6_ADDRFORM

y

y

改变套接口的地址结构

int

IPV6_CHECKSUM

y

y

原始套接口的校验和字段偏移

int

IPV6_DSTOPTS

y

y

接收目标选项

y

int

IPV6_HOPLIMIT

y

y

接收单播跳限

y

int

IPV6_HOPOPTS

y

y

接收步跳选项

y

int

IPV6_NEXTHOP

y

y

指定下一跳地址

y

sockaddr{}

IPV6_PKTINFO

y

y

接收分组信息

y

int

IPV6_PKTOPTIONS

y

y

指定分组选项

见后面说明

IPV6_RTHDR

y

y

接收原路径

y

int

IPV6_UNICAST_HOPS

y

y

缺省单播跳限

int

IPV6_MULTICAST_IF

y

y

指定外出接口

in6_addr{}

IPV6_MULTICAST_HOPS

y

y

指定外出跳限

u_int

IPV6_MULTICAST_LOOP

y

y

指定是否回馈

y

u_int

IPV6_ADD_MEMBERSHIP

y

加入多播组

ipv6_mreq{}

IPV6_DROP_MEMBERSHIP

y

离开多播组

ipv6_mreq{}

IPPROTO_TCP

TCP_KEEPALIVE

y

y

控测对方是否存活前连接闲置秒数

int

TCP_MAXRT

y

y

TCP最大重传时间

int

TCP_MAXSEG

y

y

TCP最大分节大小

int

TCP_NODELAY

y

y

禁止Nagle算法

y

int

TCP_STDURG

y

y

紧急指针的解释

y

int

3.套接字选项设置实例

#include

#include

#include

#include

#include

#include

#include

#include

//获取与设置套接字选项的函数

//定认一个共用体用于保存结果,由于返回值具有不同的类型所以必须用共用体

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选项

//设置超时间为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");

}

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

printf("x ",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、付费专栏及课程。

余额充值