相信你学过前面的 TCP 协议也知道,里头的算法非常多,有些算法你可以开启,有些你可以关闭。比如你可以设置发送和接收缓冲区大小,也可以设置是否打开 Nagle 算法等等。这些是如何做到的呢?
有很多方法可以用来获取和设置影响套接字的选项:
- getsockopt 和 setsockopt 函数
- fcntl 函数
- ioctl 函数
大多数情况,我们都使用 getsockopt 和 setsockopt 函数。
1. 套接字选项
getsockopt 和 setsockopt 函数是根据套接字的类别对其进行设置的,比如有些套接字选项和协议无关,这种就称为通用套接字选项。有些套接字选项和 TCP 协议相关,那么这种套接字选项就称为 TCP 套接字选项,比如用来打开和关闭 Nagle 算法的选项 TCP_NODELAY。
因为套接字选项的分类比较多,语言描述起来实在是太麻烦,我们用图 1 的思维导图可以一目了然。
图1 套接字选项的分类
有一些像 ipv6、sctp 这种类型的套接字,我们就不关心了,所以图 1 中只显示了一些我们比较关心的部分。毕竟套接字选项实在太多,有限的时间内根本不可能一个一个搞清楚,只有我们在用到的时候,再深入去研究它。
在后面的学习中,我会挑选几个常用的选项作为代表详细说明,比如通用套接字选项中的 SO_REUSEADDR
、SO_LINGER
等等。其它的等到以后我们知识足够用的时候再讲解。
2. setsockopt 和 getsockopt
这两个函数分别是对套接字设置和获取选项的值。函数原型如下:
#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);
参数 level 表示套接字选项的级别(类别),比如你如果要设置通用套接字选项,那么 level 就应该设置为 SOL_SOCKET,如果你要设置 TCP 套接字选项,level 就是 IPPROTO_TCP,具体可以参考图 1.
参数 optname 就是具体的选项名称了,比如你要设置 SO_REUSEADDR,你可以这样:
int reuse = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
3. 套接字选项的数据类型
不同的套接字选项数据类型都不一样,但是主要包含以下两种:
- bool 类型:主要用来打开和关闭某项功能,比如 TCP_NODELAY.
- 带有具体数值的类型:
- int : 主要用来设置某个参数的值,比如 SO_SNDBUF,它的类型就是缓冲区大小,是一个整型
- 结构体:有些套接字的参数值可能不是单纯的整数类型,可是结构体,比如 SO_LINGER,它的数据类型是 struct linger.
setsockopt 和 getsockopt 函数的第 4 个参数是一个 void* 类型,正因为套接字选项的数据类型多种多样,所以这个参数就是一个 void* 类型了,它可以接收任何类型的指针,但是同时,你还得告诉第 5 个参数这个数据类型的大小。
这么多选项的数据类型,你也记不住,图 1 中每个选项后面的小括号中都标记了选项的数据类型,可以方便查看。
4. 实例
再举两个小例子
- 比如我们要设置 SO_LINGER,那应该这样:
int ret, len; struct linger lgr; lgr.l_onoff = 1; lgr.l_linger = 10; // 设置 linger 的值 ret = setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lgr, sizeof(lgr)); if (ret < 0) { perror("setsockopt"); } // 获取 linger 的值 len = sizeof(lgr); ret = getsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lgr, &len); if (ret < 0) { perror("getsockopt"); }
- 打开 TCP_NODELAY 选项
int onoff = 1; // c 语言中不提供 bool 类型,因此只能使用 int 来替代。 setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &onoff, sizeof(onoff));
5. 总结
- 知道套接字选项有哪些类别
- 掌握 setsockopt 函数和 getsockopt 函数