您的位置:首页 > 理论基础 > 计算机网络

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

2013-11-09 14:37 399 查看
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指定外出TTLu_char
IP_MULTICAST_LOOPyy指定是否回馈u_char
IP_ADD_MEMBERSHIPy加入多播组ip_mreq{}
IP_DROP_MEMBERSHIPy离开多播组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_MEMBERSHIPy加入多播组ipv6_mreq{}
IPV6_DROP_MEMBERSHIPy离开多播组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包等方面有着重要的应用.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: