博客主页:https://blog.csdn.net/wkd_007
博客内容:嵌入式开发、Linux、C语言、C++、数据结构、音视频
本文内容:介绍
金句分享:你不能选择最好的,但最好的会来选择你——泰戈尔
⏰发布时间⏰:2024-02-27 09:02:30
本文未经允许,不得转发!!!
目录
- 一、概述
- 二、Linux系统下套接字选项
- 三、getsockopt、setsockopt 函数
- ✨3.1 getsockopt、setsockopt 函数介绍
- ✨3.2 getsockopt、setsockopt 函数举例
- 四、常见的通用套接字选项
- 五、总结
一、概述
在网络编程中,套接字选项经常需要用到,例如设置套接字缓冲区大小、设置套接字非阻塞等。在Linux中,与套接字选项相关的几个系统调用函数有:getsockopt
、setsockopt
、fcntl
、ioctl
。其中,getsockopt
、setsockopt
函数只能用于套接字选项,也是本文要求重点掌握的两个函数,而fcntl在网络编程中,最常见的就是将套接字设置成非阻塞。
二、Linux系统下套接字选项
下面两个图片是Linux系统下套接字选项汇总,级别分别有:SOL_SOCKET、IPPROTO_IP、IPPROTO_ICMPV6、IPPROTO_IPV6、IPPROTO_TCP、IPPROTO_SCTP
;数据类型中,用{}
来表示结构体,如:linger{}
表示struct linger
、timeval{}
表示struct timeval
、timeval{}
表示struct timeval
。
1、套接字层和IP层的套接字选项汇总(见下图)
2、传输层的套接字选项汇总(见下图)
三、getsockopt、setsockopt 函数
✨3.1 getsockopt、setsockopt 函数介绍
函数原型:
#include 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);
- 函数说明:
getsockopt
和setstockopt
用于获取或设置文件描述符sockfd
引用的套接字的选项。 - 函数参数:
- sockfd:要操作的文件描述符
sockfd
; - level:级别,取值一般有:
SOL_SOCKET、IPPROTO_IP、IPPROTO_ICMPV6、IPPROTO_IPV6、IPPROTO_TCP、IPPROTO_SCTP
; - optname:选项名;
- optval:指向某个变量的指针,用来存放获取或设置的值;
- optlen:指明 optval 参数指向的内存大小。
- sockfd:要操作的文件描述符
- 返回值:成功返回 0 ,出错返回 -1。
✨3.2 getsockopt、setsockopt 函数举例
下面代码是《unix网络编程卷1》源码进行修改的,可以依次打印获取到的套接字选项默认值。
// sockopt.c 修改自 《unix网络编程卷1》源码/* include checkopts1 *//* *INDENT-OFF* */#include #include #include /* for TCP_xxx defines */union val {inti_val;longl_val;struct lingerlinger_val;struct timevaltimeval_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;intopt_level;intopt_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 },#ifdefSO_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 },//{ "SO_USELOOPBACK",SOL_SOCKET,SO_USELOOPBACK,sock_str_flag },//{ "IP_TOS",IPPROTO_IP,IP_TOS,sock_str_int },//{ "IP_TTL",IPPROTO_IP,IP_TTL,sock_str_int },#ifdefIPV6_DONTFRAG{ "IPV6_DONTFRAG",IPPROTO_IPV6,IPV6_DONTFRAG,sock_str_flag },#else{ "IPV6_DONTFRAG",0,0,NULL },#endif#ifdefIPV6_UNICAST_HOPS{ "IPV6_UNICAST_HOPS",IPPROTO_IPV6,IPV6_UNICAST_HOPS,sock_str_int },#else{ "IPV6_UNICAST_HOPS",0,0,NULL },#endif#ifdefIPV6_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 },#ifdefSCTP_AUTOCLOSE{ "SCTP_AUTOCLOSE",IPPROTO_SCTP,SCTP_AUTOCLOSE,sock_str_int },#else{ "SCTP_AUTOCLOSE",0,0,NULL },#endif#ifdefSCTP_MAXBURST{ "SCTP_MAXBURST",IPPROTO_SCTP,SCTP_MAXBURST,sock_str_int },#else{ "SCTP_MAXBURST",0,0,NULL },#endif#ifdefSCTP_MAXSEG{ "SCTP_MAXSEG",IPPROTO_SCTP,SCTP_MAXSEG,sock_str_int },#else{ "SCTP_MAXSEG",0,0,NULL },#endif#ifdefSCTP_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){intfd;socklen_tlen;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;#ifdefIPV6case IPPROTO_IPV6:fd = socket(AF_INET6, SOCK_STREAM, 0);break;#endif#ifdefIPPROTO_SCTPcase IPPROTO_SCTP:fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);break;#endifdefault:printf("Can't create fd for level %d\n", ptr->opt_level);}len = sizeof(val);if (getsockopt(fd, ptr->opt_level, ptr->opt_name, &val, &len) == -1) {printf("getsockopt error");} else {printf("default = %s\n", (*ptr->opt_val_str)(&val, len));}close(fd);}}return 0;}/* end checkopts2 *//* include checkopts3 */static charstrres[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);elsesnprintf(strres, sizeof(strres), "%s", (ptr->i_val == 0) " />"off" : "on");return(strres);/* *INDENT-ON* */}/* end checkopts3 */static char*sock_str_int(union val *ptr, int len){if (len != sizeof(int))snprintf(strres, sizeof(strres), "size (%d) not sizeof(int)", len);elsesnprintf(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);elsesnprintf(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);elsesnprintf(strres, sizeof(strres), "%ld sec, %ld usec", tvptr->tv_sec, tvptr->tv_usec);return(strres);}
运行结果:
四、常见的通用套接字选项
- SO_BROADCAST:开启或禁止进程发送广播消息的能力。只有数据报套接字支持广播;
- SO_KEEPALIVE:给一个TCP套接字设置保持存活(keep-alive)选项后,如果2小时内在该套接字的任一方向上都没有数据交换,TCP就自动给对端发送一个保持存活探测分节(keep-alive probe)。
- SO_LINGER:本选项指定close函数对面向连接的协议(例如TCP和SCTP,但不是UDP)如何操作。默认操作是close立即返回,但是如果有数据残留在套接字发送缓冲区中,系统将试着把这些数据发送给对端。
- SO_RCVBUF:获取或设置接收缓冲区大小。接收缓冲区被TCP、UDP和SCTP用来保存接收到的数据,直到由应用进程来读取;
- SO_SNDBUF:获取或设置发送缓冲区大小。
- SO_RCVLOWAT:接收低水位标记。让select函数返回“可读”时套接字接收缓冲区中所需的数据量。
- SO_SNDLOWAT:发送低水位标记。让select函数返回“可写”时套接字发送缓冲区中所需的可用空间。
SO_REUSEADDR
:
(1) SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将该端口用作它们的本地端口的连接仍存在。
(2) SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。
(3) SO_REUSEADDR允许单个进程捆绑同一端口到多个套接字上,只要每次捆绑指定不同的本地P地址即可。
(4) SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口已绑定到某个套接字上时,如果传输协议支持,同样的IP地址和端口还可以捆绑到另一个套接字上。一般来说本特性仅支持UDP套接字。
五、总结
本文介绍网络编程中的套接字选项,先是汇总了常见的套接字选项,然后介绍获取和设置套接字选项的函数getsockopt、setsockopt,并给出对应的C语言例子,最后列出几个常见的通用套接字选项。
如果文章有帮助的话,点赞、收藏⭐,支持一波,谢谢