2015/12/02

设置socket选项getsockopt和setsockopt接口


getsockopt, setsockopt: get or set options on sockets.

获取或者设置套接字的选项

#include <sys/types.h>
#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:选项所在的协议层。SOL_SOCKET, IPPROTO_IP, IPPROTO_TCP

optname:需要访问的选项名。

optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲。

optlen:optval缓冲区的长度。


成功执行时,返回0。失败返回-1,errno被设为以下的某个值

EBADF:sockfd不是有效的文件描述词

EFAULT:optval指向的内存并非有效的进程空间

EINVAL:在调用setsockopt()时,optlen无效

ENOPROTOOPT:指定的协议层不能识别选项

ENOTSOCK:sockfd描述的不是套接字


level 为 SOL_SOCKET

optname desc type
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSERADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int

level 为 IPPROTO_IP

optname desc type
IP_HDRINCL 在数据包中包含IP首部 int
IP_TOS 服务类型
IP_TTL 生存时间 int

level 为 IPPRO_TCP

optname desc type
TCP_MAXSEG TCP最大数据段的大小 int
TCP_NODELAY 不使用Nagle算法 int

常见用法

//设置socket可重用

int reuse = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, sizeof(int));

//设置socket保持连接状态

int keepalive = 1;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive));

使用 getsockopt() 检测当前所有参数默认值状态

#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <stdio.h>

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

};

struct sock_opts G_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 },
    { "SO_TYPE",            SOL_SOCKET, SO_TYPE,        sock_str_int },
    { "SO_BSDCOMPAT",       SOL_SOCKET, SO_BSDCOMPAT,   sock_str_int },
    { "IP_HDRINCL",         IPPROTO_IP, IP_HDRINCL,     sock_str_int },
    { "IP_TOS",             IPPROTO_IP, IP_TOS,         sock_str_int },
    { "IP_TTL",             IPPROTO_IP, IP_TTL,         sock_str_int },
    { "TCP_MAXSEG",         IPPROTO_TCP,TCP_MAXSEG,     sock_str_int },
    { "TCP_NODELAY",        IPPROTO_TCP,TCP_NODELAY,    sock_str_flag },
    { NULL,                 0,          0,              NULL }
};

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", 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), "%ld sec, %ld usec",tvptr->tv_sec, tvptr->tv_usec);
    }
    return(strres);
}

int
main(int argc, char **argv) {
    int                 fd;
    socklen_t           len;
    struct sock_opts    *ptr;

    for (ptr = G_sock_opts; ptr->opt_str != NULL; ptr++) {
        printf("%s: ", ptr->opt_str);
        if (ptr->opt_val_str == NULL)
            printf("(undefined)\n");
        else {
            fd = socket(AF_INET, SOCK_STREAM, 0);
            len = sizeof(val);
            if (getsockopt(fd, ptr->opt_level, ptr->opt_name, &val, &len) == -1) {
                printf("get socket options err\n");
            } else {
                printf("default = %s\n", (*ptr->opt_val_str)(&val, len));
            }
            close(fd);
        }
    }

    return 0;
}

输出:

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 = 87380
SO_SNDBUF: default = 16384
SO_RCVLOWAT: default = 1
SO_SNDLOWAT: default = 1
SO_RCVTIMEO: default = 0 sec, 0 usec
SO_SNDTIMEO: default = 0 sec, 0 usec
SO_REUSEADDR: default = off
SO_TYPE: default = 1
SO_BSDCOMPAT: default = 0
IP_HDRINCL: default = 0
IP_TOS: default = 0
IP_TTL: default = 64
TCP_MAXSEG: default = 536
TCP_NODELAY: default = off