ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux socket编程 套接字选项

2021-06-07 02:03:33  阅读:275  来源: 互联网

标签:socket int IP IPV6 SO Linux IPPROTO 接字 SCTP


1. 套接字选项概述

有很多方法来获取和设置套接字的选项, 以影响套接字行为:

  • getsockopt和setsocketopt;
  • fcntl;
  • ioctl;

2. getsockopt和setsockopt

2个函数仅用于套接字, 分别用于获取和设置套接字选项

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

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);
  • 参数
    sockfd 一个打开的套接字描述符
    level 级别, 指定系统中解释选项的代码或为通用套接字代码, 或为某个特定于协议的代码(e.g. IPv4/6, TCP, STCP)
    optval 指向某个变量(optval)的指针, setsockopt从optval中取得选项待设置的新值, getsockopt则把已获得的选项当前值存放到*optval中
    optlen 对setsockopt是值参数, 对getsockopt是值-结果参数, 用于指明optval的大小

套接字选项粗分为2个基本类型: 启用或进制某个特性的二元选项, 称为标志选项; 取得并返回我们可以设置或检查的特定值的选项, 称为值选项.

套接字层和IP层的套接字选项汇总表:

leve级别 optname选项名 get set 说明 标志 数据类型
SOL_SOCKET SO_BROADCAST · · 允许发送广播数据报 · int
SOL_SOCKET SO_DEBUG · · 开启调试跟踪 · int
SOL_SOCKET SO_DONTROUTE · · 绕过外出路由表查询 · int
SOL_SOCKET SO_ERROR · 获取待处理错误并清除 int
SOL_SOCKET SO_KEEPALIVE · · 周期性测试连接是否仍存活 · int
SOL_SOCKET SO_LINEGER · · 若有数据待发送, 则延迟关闭 linger{}
SOL_SOCKET SO_OOBINLINE · · 让接收到的带外数据继续在线留存 · int
SOL_SOCKET SO_RCVBUF · · 接收缓冲区大小 int
SOL_SOCKET SO_SNDBUF · · 发送缓冲区大小 int
SOL_SOCKET SO_RCVLOWAT · · 接收缓冲区低水位标记 int
SOL_SOCKET SO_SNDLOWAT · · 发送缓冲区低水位标记 int
SOL_SOCKET SO_RCVTIMEO · · 接收超时 timeval{}
SOL_SOCKET SO_SNDTIMEO · · 发送超时 timeval{}
SOL_SOCKET SO_REUSEADDR · · 允许重用本地地址 · int
SOL_SOCKET SO_REUSEPORT · · 允许重用本地端口 · int
SOL_SOCKET SO_TYPE · 取得套接字类型 int
SOL_SOCKET SO_USELOOPBACK · · 路由套接字取得所发送数据的副本 · int
IPPROTO_IP IP_HDRINCL · · 随数据包含的IP首部 · int
IPPROTO_IP IP_OPTIONS · · IP首部选项
IPPROTO_IP IP_RECVDSTADDR · · 返回目的IP地址 · int
IPPROTO_IP IP_RECVIF · · 返回接收接口索引 · int
IPPROTO_IP IP_TOS · · 服务类型和优先权 int
IPPROTO_IP IP_TTL · · 存活时间 int
IPPROTO_IP IP_MULTICAST_IP · · 指定外出接口 in_addr{}
IPPROTO_IP IP_MULTICAST_TTL · · 指定外出TTL u_char
IPPROTO_IP IP_MULTICAST_LOOP · · 指定是否环回 u_char
IPPROTO_IP IP_ADD_MEMBERSHIP · 加入多播组 ip_mreq{}
IPPROTO_IP IP_DROP_MEMBERSHIP · 离开多播组 ip_mreq{}
IPPROTO_IP IP_BLOCK_SOURCE · 阻塞多播源 ip_mreq_source{}
IPPROTO_IP IP_UNBLOCK_SOURCE · 开通多播源 ip_mreq_source{}
IPPROTO_IP IP_ADD_SOURCE_MEMBERSHIP · 加入源特定多播组 ip_mreq_source{}
IPPROTO_IP IP_DROP_SOURCE_MEMBERSHIP · 离开源特定多播组 ip_mreq_source{}
IPPROTO_ICMPV6 ICMP6_FILTER · · 指定待传递的ICMPv6消息类型 icmp6_filter{}
IPPROTO_IPV6 IPV6_CHECKSUM · · 用于原始套接字的校验和字段偏移 int
IPPROTO_IPV6 IPV6_DONTFRAG · · 丢弃大的分组而非将其分片 · int
IPPROTO_IPV6 IPV6_NEXTHOP · · 指定下一跳地址 sockaddr_in6{}
IPPROTO_IPV6 IPV6_PATHMTU · 获取当前路径MTU ip6_mtuinfo{}
IPPROTO_IPV6 IPV6_RECVDSTOPTS · · 接收目的地选项 · int
IPPROTO_IPV6 IPV6_RECVHOPLIMIT · · 接收单播跳限 · int
IPPROTO_IPV6 IPV6_RECVHOPOPTS · · 接收步跳选项 · int
IPPROTO_IPV6 IPV6_RECVPATHMTU · · 接收路径MTU · int
IPPROTO_IPV6 IPV6_RECVPKTINFO · · 接收分组信息 · int
IPPROTO_IPV6 IPV6_RECVRTHDR · · 接收源路径 · int
IPPROTO_IPV6 IPV6_RECVTCLASS · · 接收流通类别 · int
IPPROTO_IPV6 IPV6_UNICAST_HOPS · · 默认单播跳限 int
IPPROTO_IPV6 IPV6_USE_MIN_MTU · · 使用最小MTU · int
IPPROTO_IPV6 IPV6_V6ONLY · · 禁止v4兼容 · int
IPPROTO_IPV6 IPV6_XXX · · 黏附性辅助数据
IPPROTO_IPV6 IPV6_MULTICAST_IF · · 指定外出接口 u_int
IPPROTO_IPV6 IPV6_MULTICAST_HOPS · · 指定外出跳限 int
IPPROTO_IPV6 IPV6_MULTICAST_LOOP · · 指定是否环回 · u_int
IPPROTO_IPV6 IPV6_JOIN_GROUP · 加入多播组 ipv6_mreq{}
IPPROTO_IPV6 IPV6_LEAVE_GROUP · 离开多播组 ipv6_mreq{}
IPPROTO_IP或
IPPROTO_IPV6
MCAST_JOIN_GROUP · 加入多播组 group_req{}
IPPROTO_IP或
IPPROTO_IPV6
MCAST_LEAVE_GROUP · 离开多播组 group_source_req{}
IPPROTO_IP或
IPPROTO_IPV6
MCAST_BLOCK_GROUP · 阻塞多播组 group_source_req{}
IPPROTO_IP或
IPPROTO_IPV6
MCAST_UNBLOCK_GROUP · 开通多播组 group_source_req{}
IPPROTO_IP或
IPPROTO_IPV6
MCAST_JOIN_SOURCE_GROUP · 加入源特定多播组 group_source_req{}
IPPROTO_IP或
IPPROTO_IPV6
MCAST_LEAVE_SOURCE_GROUP · 离开源特定多播组 group_source_req{}
IPPROTO_TCP TCP_MAXSEG · · TCP最大分节大小 int
IPPROTO_TCP TCP_NODELAY · · 禁止Nagle算法 · int
IPPROTO_SCTP SCTP_ADAPTION_LAYER · · 适配层指示 stcp_setadaption{}
IPPROTO_SCTP SCTP_ASSOCINFO · 检查并设置关联信息 stcp_assocparams{}
IPPROTO_SCTP SCTP_AUTOCLOSE · · 自动关闭操作 int
IPPROTO_SCTP SCTP_DEFAULT_SEND_PARAM · · 默认发送参数 stcp_sndrcvinfo{}
IPPROTO_SCTP SCTP_DISABLE_FRAGMENTS · · SCTP分片 · int
IPPROTO_SCTP SCTP_EVENTS · · 感兴趣事件的通知 sctp_event_subscribe{}
IPPROTO_SCTP SCTP_GET_PEER_ADDR_INFO 获取对端地址状态 sctp_paddrinfo{}
IPPROTO_SCTP SCTP_I_WANT_MAPPED_V4_ADDR · · 映射的v4地址 · int
IPPROTO_SCTP SCTP_INTTMSG · · 默认的INIT参数 sctp_initmsg{}
IPPROTO_SCTP SCTP_MAXBURST · · 最大猝发大小 int
IPPROTO_SCTP SCTP_MAXSEG · · 最大分片大小 int
IPPROTO_SCTP SCTP_NODELAY · · 禁止Nagle算法 · int
IPPROTO_SCTP SCTP_PEER_ADDR_PARAMS · 对端地址参数 sctp_paddrparams{}
IPPROTO_SCTP SCTP_PRIMARY_ADDR · 主目的地址 sctp_setprim{}
IPPROTO_SCTP SCTP_RTOINFO · RTO信息 sctp_rtoinfo{}
IPPROTO_SCTP SCTP_SET_PEER_PRIMARY_ADDR · 对端的主目的地址 sctp_setpeerprim{}
IPPROTO_SCTP SCTP_STATUS · 获取关联状态 sctp_status{}

说明:

  1. 数据类型形如linger{}, 表示struct linger;
  2. 标有"标志"的列指出一个选项是否为标志选项. 当给这些标志选项调用getsockopt函数时, *optval是一个整数. *optval中返回的值为0, 表示相应的选项被禁止, 不为0表示相应的选项被启用;
  3. setsockopt函数需要一个不为0 的optval值来启用选项, 为0 的optval来禁止选项;
  4. 如果"标志"列不含"·", 那么相应选项用于在用户进程与系统之间传递所指定数据类型的值;

3. 检查选项是否受支持并获取默认值

思路: 利用getsockopt 获取每个level对应的选项名, 如果没有出错, 则证明支持该选项; 如果出错, 则说明不支持. 如果读取出来的值大小, 跟选项汇总表不一致, 则表明可能有哪里出问题, 打印出来.
一个sockopt的判断伪代码

sockfd = socket();

if (getsockopt(fd, opt_level, opt_name, &val, &len) < 0) {
    // getsockopt error
    ...
}
else {
// no getsockopt error
// print opt_val_str
}

如果IPV6_DONTFRAG, SCTP_AUTOCLOSE等宏定义无法识别, 请确认安装好libsctp-dev, lksctp-tools库后, 然后#include <netinet/sctp.h>
安装命令, 详见 Unix下基于SCTP socket的通信:一对一场景 | 简书

$ sudo apt-get install libsctp-dev lksctp-tools

源代码:

#include <sys/types.h> 
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/sctp.h>

/* UNP官方代码 */
#include "ourhdr.h" 

union val {
    int i_val;
    long l_val;
    struct linger linger_val;
    struct timeval timeval_val;
} val;

int Socket(int domain, int type, int protocol);
static char *sock_str_flag(union val *, int);
static char *sock_str_int(union val *, int);
static char *sock_str_linger(union val *, int);
static char *sock_str_timeval(union val *, int);

/**
* 自定义结构sock_opts, 包含了获得或输出套接字选项的所有信息
*/
struct sock_opts {
    const char *opt_str; /// 字符名称
    int opt_level; /// 级别
    int opt_name;  /// 名称
    char *(*opt_val_str)(union val *, int); /// 函数指针, 用于输出
} sock_opts[] = {
    {"SO_BROADCAST",  SOL_SOCKET,  SO_BROADCAST,     sock_str_flag},
    {"SO_DEBUG",      SOL_SOCKET,  SO_DEBUG,         sock_str_flag},
    {"SO_DONTROUTE",  SOL_SOCKET,  SO_DONTROUTE,     sock_str_flag},
    {"SO_ERROR",      SOL_SOCKET,  SO_ERROR,         sock_str_int },
    {"SO_KEEPALIVE",  SOL_SOCKET,  SO_KEEPALIVE,     sock_str_flag},
    {"SO_LINGER",     SOL_SOCKET,  SO_LINGER,        sock_str_linger},
	{"SO_OOBINLINE",  SOL_SOCKET,  SO_OOBINLINE,     sock_str_flag},
	{"SO_RCVBUF",     SOL_SOCKET,  SO_RCVBUF,        sock_str_int },
	{"SO_SNDBUF",     SOL_SOCKET,  SO_SNDBUF,        sock_str_int },
	{"SO_RCVLOWAT",   SOL_SOCKET,  SO_RCVLOWAT,      sock_str_int },
	{"SO_SNDLOWAT",   SOL_SOCKET,  SO_SNDLOWAT,      sock_str_int },
	{"SO_RCVTIMEO",   SOL_SOCKET,  SO_RCVTIMEO,      sock_str_timeval },
	{"SO_SNDTIMEO",   SOL_SOCKET,  SO_SNDTIMEO,      sock_str_timeval },

	{"SO_REUSEADDR",  SOL_SOCKET,  SO_REUSEADDR,     sock_str_flag },
#ifdef SO_REUSEPORT
	{"SO_REUSEPORT",  SOL_SOCKET,  SO_REUSEPORT,     sock_str_flag},
#else
	{"SO_REUSEPORT",  0,  		   0,     			 NULL},
#endif

	{"SO_TYPE",  	  SOL_SOCKET,  SO_TYPE,       	 sock_str_int },
#ifdef SO_USELOOPBACK
	{"SO_USELOOPBACK",SOL_SOCKET,  SO_USELOOPBACK,   sock_str_flag},
#else
	{"SO_USELOOPBACK",  0,  		   0,     			 NULL},
#endif

	{"IP_TOS",  	  IPPROTO_IP,  IP_TOS,     	     sock_str_int },
	{"IP_TTL",  	  IPPROTO_IP,  IP_TTL,  	     sock_str_int },

	{"IPV6_DONTFRAG", 		IPPROTO_IPV6, IPV6_DONTFRAG,    	 sock_str_flag},
	{"IPV6_UNICAST_HOPS",  	IPPROTO_IPV6, IPV6_UNICAST_HOPS,     sock_str_int },
	{"IPV6_V6ONLY",  		IPPROTO_IPV6, IPV6_V6ONLY,     		 sock_str_flag},
	
	{"TCP_MAXSEG",   IPPROTO_TCP,  TCP_MAXSEG,        sock_str_int },
	{"TCP_NODELAY",  IPPROTO_TCP,  TCP_NODELAY,      sock_str_flag},

	{"SCTP_AUTOCLOSE",  IPPROTO_SCTP,  SCTP_AUTOCLOSE,     sock_str_int },
#ifdef SCTP_MAXBURST
	{"SCTP_MAXBURST",   IPPROTO_SCTP,  SCTP_MAXBURST,      sock_str_int },
#else
	{"SCTP_MAXBURST",  0,  		   0,     			 NULL},
#endif

	{"SCTP_MAXSEG",  	IPPROTO_SCTP,  SCTP_MAXSEG,    	   sock_str_int },
	{"SCTP_NODELAY",    IPPROTO_SCTP,  SCTP_NODELAY,       sock_str_flag},

	{NULL, 				0, 			   0, 				   NULL}
};

int main()
{
    int fd;
    socklen_t len;
    struct sock_opts *ptr;

    for (ptr = sock_opts; ptr->opt_str != NULL; ++ptr) {
        printf("%s: ", ptr->opt_str);
        if (ptr->opt_val_str == NULL) {
            printf("(undefined)\n");
        }
        else {
            switch(ptr->opt_level) {
            case SOL_SOCKET:
            case IPPROTO_IP:
            case IPPROTO_TCP:
                fd = Socket(AF_INET, SOCK_STREAM, 0);
                break;

#ifdef IPV6
            case IPPROTO_IPV6:
                fd = Socket(AF_INET6, SOCK_STREAM, 0);
                break;
#endif
#ifdef IPPROTO_SCTP
            case IPPROTO_SCTP:
                fd = Socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
                break;
#endif
            default:
                // err_quit("Can't create fd for level %d\n", ptr->opt_level);
				fprintf(stderr, "Can't create fd for level %d\n", ptr->opt_level);
            }

            len = sizeof(val);
			
			// if (fd < 0) continue;

            if (getsockopt(fd, ptr->opt_level, ptr->opt_name, &val, &len) == -1) {
                err_ret("getsockopt error");
            }
            else {
                printf("default = %s\n", (*ptr->opt_val_str)(&val, len));
            }

            close(fd);
        }
    }
    
    return 0;
}

int Socket(int domain, int type, int protocol)
{
    int fd = socket(domain, type, protocol);

    if (fd < 0) {
        perror("socket error");
        return -1;
    }

    return fd;
}


static char strres[128];
static char *sock_str_flag(union val *ptr, int len) 
{
    if (len != sizeof(int)) 
        snprintf(strres, sizeof(strres), "size (%d) not sizeof(int)", len);
    else 
        snprintf(strres, sizeof(strres), "%s", (ptr->i_val == 0) ? "off": "on");

    return strres;
}

static char *sock_str_int(union val *ptr, int len)
{
    if (len != sizeof(int)) 
        snprintf(strres, sizeof(strres), "size (%d) not sizeof(int)", len);
    else 
        snprintf(strres, sizeof(strres), "%d\t", ptr->i_val);

    return strres;
}

static char *sock_str_linger(union val *ptr, int len)
{
    if (len != sizeof(struct linger)) 
        snprintf(strres, sizeof(strres), "size (%d) not sizeof(struct linger)", len);
    else 
        snprintf(strres, sizeof(strres), "l_onoff = %d, l_linger = %d\t", ptr->linger_val.l_onoff, ptr->linger_val.l_linger);

    return strres;
}

static char *sock_str_timeval(union val *ptr, int len)
{
    if (len != sizeof(struct timeval)) 
        snprintf(strres, sizeof(strres), "size (%d) not sizeof(struct timeval)", len);
    else 
        snprintf(strres, sizeof(strres), "%ld sec, %ldusec\t", ptr->timeval_val.tv_sec, ptr->timeval_val.tv_usec);

    return strres;
}

运行结果:

SO_BROADCAST: default = off
SO_DEBUG: default = off
SO_DONTROUTE: default = off
SO_ERROR: default = 0	
SO_KEEPALIVE: default = off
SO_LINGER: default = l_onoff = 0, l_linger = 0	
SO_OOBINLINE: default = off
SO_RCVBUF: default = 131072	
SO_SNDBUF: default = 16384	
SO_RCVLOWAT: default = 1	
SO_SNDLOWAT: default = 1	
SO_RCVTIMEO: default = 0 sec, 0usec	
SO_SNDTIMEO: default = 0 sec, 0usec	
SO_REUSEADDR: default = off
SO_REUSEPORT: default = off
SO_TYPE: default = 1	
SO_USELOOPBACK: (undefined)
IP_TOS: default = 0	
IP_TTL: default = 64	
Can't create fd for level 41
IPV6_DONTFRAG: getsockopt error: Bad file descriptor
Can't create fd for level 41
IPV6_UNICAST_HOPS: getsockopt error: Bad file descriptor
Can't create fd for level 41
IPV6_V6ONLY: getsockopt error: Bad file descriptor
TCP_MAXSEG: default = 536	
TCP_NODELAY: default = off
SCTP_AUTOCLOSE: default = 0	
SCTP_MAXBURST: (undefined)
SCTP_MAXSEG: default = size (8) not sizeof(int)
SCTP_NODELAY: default = off

标签:socket,int,IP,IPV6,SO,Linux,IPPROTO,接字,SCTP
来源: https://www.cnblogs.com/fortunely/p/14856765.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有