套接字选项的查询与设置 -- getsockopt、setsockopt

2015-01-11 18:40:12   最后更新: 2015-01-11 18:40:12   访问数量:1249




有很多方法可以影响套接字选项:

  1. getsockopt、setsockopt
  2. fcntl
  3. ioctl

getsockopt 和 setsockopt 两个函数只用于套接字

函数原型

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);

 

两个函数具有相同的参数,都定义在 sys/socket.h 中

调用成功返回0,否则返回-1

调用参数

  • sockfd -- 已经打开的套接字描述符
  • level -- 系统中解释选项的代码,或通用套接字代码,或某个特定协议的代码
  • optval -- 指向某个变量的指针
  • optlen -- optval 指向变量的大小,对于 getsockopt 来说是一个值-结果参数

套接字选项汇总

套接字层和IP层的套接字选项汇总
leveloptnamegetset说明标志数据类型
SOL_SOCKETSO_BROADCAST允许发送广播数据报int
SO_DEBUG开启调试跟踪int
SO_DONTROUTE绕过外出路由表查询int
SO_ERROR 获取待处理错误并清除 int
SO_KEEPALIVE周期性的测试连接是否存活int
SO_LINGER若有数据待发送则延迟关闭 linger{}
SO_OOBINLINE让接收到的带外数据继续在线留存int
SO_RCVBUF接收缓冲区大小 int
SO_SNDBUF发送缓冲区大小 int
SO_RCVLOWAT接收缓冲区低水位标记 int
SO_SNDLOWAT发送缓冲区大小 int
SO_RCVTIMEO接收超时 timeval{}
SO_SNDTIMEO发送超时 timeval{}
SO_REUSEADDR允许重用本地地址int
SO_REUSEPORT允许重用本地端口int
SO_TYPE 取得套接字类型 int
SO_USELOOPBACK路由套接字取得所发送数据的副本int
IPPROTO_IPIP_HDRINCL随数据包含的IP首部int
IP_OPTIONSIP首部选项  
IP_RECVDSTADDR返回目的IP地址int
IP_RECVIF返回接收接口索引int
IP_TOS服务类型和优先权 int
IP_TTL存活时间 int
IP_MULTICAST_IF指定外出接口 in_addr{}
IP_MULTICAST_TTL指定外出TTL u_char
IP_MULTICAST_LOOP指定是否还回 u_char
IP_ADD_MEMBERSHIP 加入多播组 ip_mreq{}
IP_DROP_MEMBERSHIP 退出多播组 ip_mreq{}
IP_BLOCK_SOURCE 阻塞多播组 ip_mreq_source{}
IP_UNBLOCK_SOURCE 解除阻塞多播组 ip_mreq_source{}
IP_ADD_SOURCE_MEMBERSHIP 加入源特定多播组 ip_mreq_source{}
IP_DROP_SOURCE_MEMBERSHIP 离开源特定多播组 ip_mreq_source{}
/* * author: 龙泉居士 * file: main.c * date: 2015-01-11 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <sys/time.h> #include <sys/un.h> #include <sys/socket.h> #include <netinet/tcp.h> #include <netinet/in.h> // getsockopt 可能返回的数据类型 union val { int i_val; long l_val; struct linger linger_val; struct timeval timeval_val; } val; 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); 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 }, #ifdef IPV6_DONTFRAG { "IPV6_DONTFRAG", IPPROTO_IPV6,IPV6_DONTFRAG, sock_str_flag }, #else { "IPV6_DONTFRAG", 0, 0, NULL }, #endif #ifdef IPV6_UNICAST_HOPS { "IPV6_UNICAST_HOPS", IPPROTO_IPV6,IPV6_UNICAST_HOPS,sock_str_int }, #else { "IPV6_UNICAST_HOPS", 0, 0, NULL }, #endif #ifdef IPV6_V6ONLY { "IPV6_V6ONLY", IPPROTO_IPV6,IPV6_V6ONLY, sock_str_flag }, #else { "IPV6_V6ONLY", 0, 0, NULL }, #endif { "TCP_MAXSEG", IPPROTO_TCP,TCP_MAXSEG, sock_str_int }, { "TCP_NODELAY", IPPROTO_TCP,TCP_NODELAY, sock_str_flag }, #ifdef SCTP_AUTOCLOSE { "SCTP_AUTOCLOSE", IPPROTO_SCTP,SCTP_AUTOCLOSE,sock_str_int }, #else { "SCTP_AUTOCLOSE", 0, 0, NULL }, #endif #ifdef SCTP_MAXBURST { "SCTP_MAXBURST", IPPROTO_SCTP,SCTP_MAXBURST, sock_str_int }, #else { "SCTP_MAXBURST", 0, 0, NULL }, #endif #ifdef SCTP_MAXSEG { "SCTP_MAXSEG", IPPROTO_SCTP,SCTP_MAXSEG, sock_str_int }, #else { "SCTP_MAXSEG", 0, 0, NULL }, #endif #ifdef SCTP_NODELAY { "SCTP_NODELAY", IPPROTO_SCTP,SCTP_NODELAY, sock_str_flag }, #else { "SCTP_NODELAY", 0, 0, NULL }, #endif { NULL, 0, 0, NULL } }; /* *INDENT-ON* */ /* end checkopts1 */ /* include checkopts2 */ int main(int argc, char **argv) { 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); if (fd < 0) { perror("socket"); exit(-1); } break; #ifdef IPV6 case IPPROTO_IPV6: fd = socket(AF_INET6, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); exit(-1); } break; #endif #ifdef IPPROTO_SCTP case IPPROTO_SCTP: fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); if (fd < 0) { perror("socket"); exit(-1); } break; #endif default: printf("Can't create fd for level %d\n", ptr->opt_level); exit(-1); } len = sizeof(val); if (getsockopt(fd, ptr->opt_level, ptr->opt_name, &val, &len) == -1) { perror("getsockopt"); exit(-1); } else { printf("default = %s\n", (*ptr->opt_val_str)(&val, len)); } close(fd); } } exit(0); } /* end checkopts2 */ /* include checkopts3 */ static char strres[128]; static char * sock_str_flag(union val *ptr, int len) { /* *INDENT-OFF* */ 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", ptr->i_val); return(strres); } static char * sock_str_linger(union val *ptr, int len) { struct linger *lptr = &ptr->linger_val; 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", lptr->l_onoff, lptr->l_linger); return(strres); } static char * sock_str_timeval(union val *ptr, int len) { struct timeval *tvptr = &ptr->timeval_val; if (len != sizeof(struct timeval)) snprintf(strres, sizeof(strres), "size (%d) not sizeof(struct timeval)", len); else snprintf(strres, sizeof(strres), "%d sec, %d usec", (int)tvptr->tv_sec, (int)tvptr->tv_usec); return(strres); }

 

程序中,首先定义了一个 union 类型,用来声明所有的 getsocketopt 可能返回的数据类型,并作为返回值的联合体

然后,定义了一个结构体数组,保存所有socket选项的名称、level以及回调函数

之后,遍历所有选项,创建socket,然后调用 getsockopt,得到 socket 选项的默认值,并调用回调函数输出选项的默认值

 

程序执行结果入下:

 

对于某些套接字选项,针对套接字的状态,是有时序上的考虑的

以下套接字选项是由 TCP 已连接套接字从监听套接字继承来的:

  • SO_DEBUG
  • SO_DONTROUTE
  • SO_KEEPALIVE
  • SO_LINGER
  • SO_OOBINLINE
  • SO_RCVBUF
  • SO_RCVLOWAT
  • SO_SNDBUF
  • SO_SNDLOWAT
  • TCP_MAXSEG
  • TCP_NODELAY

对于这些选项,accept 一直到 TCP 层完成三次握手后才会给服务器返回已连接套接字,如果想在三次握手完成时,确保上述的某个套接字选项是已设置的,那么,必须先设置监听套接字

 






读书笔记      技术帖      linux      unix      unp      unix网络编程      socket      龙潭书斋      sockopt      getsockopt      setsockopt      c预言     


京ICP备15018585号