有很多方法来获取和设置套接字的选项, 以影响套接字行为:
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);
套接字选项粗分为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{} |
说明:
思路: 利用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