基本概念:

SO_REUSEADDR套接字选项能起到以下4个不同的功用:

(1)SO_REUSEADDR允许启动一个监听服务器并捆绑众所周知端口,即使以前建立的该端口用作它们的本地端口的连接仍存在。

这个条件通常是这样碰到的:

a)启动一个监听服务器;

b)连接请求到的,派生一个子进程来处理这个客户;

c)监听服务器终止,但子进程继续为现有的连接上的客户提供服务;

d)重启监听服务器。

默认情况下,当监听服务器在步骤d调用socket、bind和listen重新启动时,由于它试图捆绑一个现有连接上的端口,从而bind调用会失败。但是如果该服务器在socket和bind两个调用之间设置了SO_REUSEADDR套接字选项,那么bind将成功。所有TCP服务器都应该指定本套接字选项,以允许服务器在这种情形下被重新启动。

(2)SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。

(3)SO_REUSEADDR允许单个进程捆绑同一个端口到多个套接字上,只要每次捆绑指定不同的本地IP地址即可。

(4)SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口已绑定到某个套接字上时。如果传输协议支持,同样的IP地址和端口还可以捆绑到另一个套接字上。一般来说,本特性仅支持UDP套接字。

SO_REUSEPORT套接字选项能起到以下2个不同的功用:

(1)本选项允许完全重复的捆绑,不过只有在想要捆绑同一IP地址和端口的每个套接字都指定了本套接字选项才行。

(2)如果被捆绑的IP地址是一个多播地址,那么SO_REUSEADDR和SO_REUSEPORT被认为是等效的。

这里我们的重点讨论内容是:

SO_REUSEADDR的第(1)个功用(蓝色字体)

SO_REUSEPORT的第(1)个功用(蓝色字体)

应用场景:nginx平滑升级就应用到了相关知识。

例子1:

我们需要知道,

a) 如果不对TCP的套接字选项进行任何限制时,如果启动两个进程,第二个进程就会在调用bind函数的时候出错(Address already in use)。

b) 如果在调用bind之前我们设置了SO_REUSEADDR,但是不在第二个进程启动前close这个套接字,那么第二个进程仍然会在调用bind函数的时候出错(Address already in use)。

c)如果在调用bind之前我们设置了SO_REUSEADDR,并接收了一个客户端连接,并且在第二个进程启动前关闭了bind的套接字,这个时候第一个进程只拥有一个套接字(与客户端的连接),那么第二个进程则可以bind成功,符合预期。

代码:

[cpp] view plain copy
  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. #include <errno.h>
  6. #include <arpa/inet.h>
  7. #include <stdlib.h>
  8. void Perror(const char *s)
  9. {
  10. perror(s);
  11. exit(EXIT_FAILURE);
  12. }
  13. int main()
  14. {
  15. int sockfd = socket(AF_INET, SOCK_STREAM, 0); //TCP
  16. int backlog = 100;
  17. short port = 9527; //端口
  18. struct sockaddr_in servaddr;
  19. servaddr.sin_family = AF_INET; //IPv4
  20. servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //表示由内核去选择IP地址
  21. servaddr.sin_port = htons(port);
  22. int flag = 1;
  23. if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag))) {
  24. Perror("setsockopt fail");
  25. }
  26. int res = bind(sockfd, (sockaddr *)&servaddr, sizeof(servaddr));
  27. if (0 == res)
  28. printf("server bind success, 0.0.0.0:%d\n", port);
  29. else {
  30. Perror("bind fail");
  31. }
  32. if (-1 == listen(sockfd, backlog)) {
  33. Perror("listen fail");
  34. }
  35. //等待连接
  36. struct sockaddr_in cliaddr;
  37. socklen_t len = sizeof(cliaddr);
  38. int connfd = accept(sockfd, (sockaddr *)&cliaddr, &len);
  39. if (-1 == connfd) {
  40. Perror("accept fail");
  41. }
  42. //解析客户端地址
  43. char buff[INET_ADDRSTRLEN + 1] = {0};
  44. inet_ntop(AF_INET, &cliaddr.sin_addr, buff, INET_ADDRSTRLEN);
  45. uint16_t cli_port = ntohs(cliaddr.sin_port);
  46. printf("connection from %s, port %d\n", buff, cli_port);
  47. //关闭bind的sockfd
  48. close(sockfd);
  49. //
  50. sleep(1200);
  51. return 0;
  52. }

编译:

g++ server.cpp -o s1

g++ server.cpp -o s2

运行结果:

例子2,

SO_REUSEPORT可能在比较旧的内核版本上不支持,我的测试环境的内核版本是3.10,相对SO_REUSEADDR来说,SO_REUSEPORT没有那么多的限制条件,允许两个毫无血缘关系的进程使用相同的IP地址同时监听同一个端口,并且不会出现惊群效应。

代码:

[cpp] view plain copy
  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <unistd.h>
  4. #include <stdio.h>
  5. #include <errno.h>
  6. #include <arpa/inet.h>
  7. #include <stdlib.h>
  8. void Perror(const char *s)
  9. {
  10. perror(s);
  11. exit(EXIT_FAILURE);
  12. }
  13. int main()
  14. {
  15. int sockfd = socket(AF_INET, SOCK_STREAM, 0); //TCP
  16. int backlog = 100;
  17. short port = 9527; //端口
  18. struct sockaddr_in servaddr;
  19. servaddr.sin_family = AF_INET; //IPv4
  20. servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //表示由内核去选择IP地址
  21. servaddr.sin_port = htons(port);
  22. int flag = 1;
  23. if (-1 == setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &flag, sizeof(flag))) {
  24. Perror("setsockopt fail");
  25. }
  26. int res = bind(sockfd, (sockaddr *)&servaddr, sizeof(servaddr));
  27. if (0 == res)
  28. printf("server bind success, 0.0.0.0:%d\n", port);
  29. else {
  30. Perror("bind fail");
  31. }
  32. if (-1 == listen(sockfd, backlog)) {
  33. Perror("listen fail");
  34. }
  35. //等待连接
  36. while (1) {
  37. struct sockaddr_in cliaddr;
  38. socklen_t len = sizeof(cliaddr);
  39. int connfd = accept(sockfd, (sockaddr *)&cliaddr, &len);
  40. if (-1 == connfd) {
  41. Perror("accept fail");
  42. }
  43. //解析客户端地址
  44. char buff[INET_ADDRSTRLEN + 1] = {0};
  45. inet_ntop(AF_INET, &cliaddr.sin_addr, buff, INET_ADDRSTRLEN);
  46. uint16_t cli_port = ntohs(cliaddr.sin_port);
  47. printf("connection from %s, port %d\n", buff, cli_port);
  48. }
  49. return 0;
  50. }

编译:

g++ server.cpp -o s1

g++ server.cpp -o s2

运行结果:

TCP/IP编程之SO_REUSEADDR和SO_REUSEPORT套接字选项相关推荐

  1. 教你使用 SO_REUSEPORT 套接字选项提升服务性能

    前言 Linux 网络栈中有一个相对较新的特性--SO_REUSEPORT 套接字选项,可以使用它来提升你的服务性能. 图 1: 上面的服务是使用并行监听器来避免请求连接瓶颈,而下面的服务只使用一个监 ...

  2. TCP/IP编程之getsockopt/setsockopt函数详解

    前述: 有很多方法来获取和设置影响套接字的选项: · getsockopt和setsockopt函数 · fcntl函数,是把套接字设置为非阻塞式I/O型或者信号驱动式I/O型以及设置套接字属主的PO ...

  3. TCP/IP编程之accept函数详解

    accept函数由TCP服务器调用,用于从已完成连接队列返回下一个已完成连接.如果已完成连接队列为空,那么进程被投入睡眠(假定套接字默为默认的阻塞方式) 函数原型: ACCEPT(2) Linux P ...

  4. TCP/IP编程之select函数详解

    前述: linux下的I/O复用模型目前很多都已经不用select函数了,而是用epoll,但是为什么还需要了解select编程呢,其实是从两个方面考虑的:一是为了通过select去理解epoll,而 ...

  5. 常用套接字选项(SOL_SOCKET级别)

    setsockopt方法 [cpp] view plaincopyprint? #include int setsockopt( int socket, int level, int option_n ...

  6. TCP网络编程之chat聊天室

    这一节我们再讲一个tcp长连接的例子,实现网络聊天室的基本功能. 聊天室的基本原理:采用Client/Server TCP架构,客户端发送消息给服务器,服务器再把消息转发给所有的客户端. 一.需求分析 ...

  7. 字选项套接字选项-java教程

    今天一直在研究字选项之类的问题,现在正好有机会和大家分享一下. ================================================  套接字机制供给了两个套接字选项接口来 ...

  8. linux 套接字选项定义

    2019独角兽企业重金招聘Python工程师标准>>> linux 套接字选项定义 2011-07-28 14:31 1.closesocket(一般不会立即关闭而经历TIME_WA ...

  9. socket套接字选项

    socket.setsockopt(level, optname, value) ↑ ↑ ↑ →[SO_KEEPALIVE ,   ↑  SO_LINGER , ↑   SO_RCVBUF , ↑  ...

最新文章

  1. 美国雷神公司对第六代战斗机的任务系统提出六点预测
  2. 威胁报告:mDNS 反射式 DDoS 攻击
  3. android系统内置HttpClient库(WebView+ Http(s)URLConnection(ok-http)+ HttpClient(apache-http))
  4. 02.操作系统概述.md
  5. 参数整定临界比例度实验_实验低温浴“秘方”,调节两个溶剂的比例实现零下10度到78度定温配制...
  6. c语言a 寻路算法,JS/HTML5游戏常用算法之路径搜索算法 A*寻路算法完整实例
  7. cgo的效率 golang_golang CGO FAQ TIPS : cgo 从 C 传递 slice 到 go
  8. endnote如何添加网页类参考文献
  9. 无监督和有监督的区别_机器学习和人工智能之间的区别
  10. CCF CSP 201604-1 折点计数
  11. localhost改ip访问
  12. 两个实用型教学网站:菜鸟教程与W3School,估计是国内做得做好的了
  13. 如何去到Jquery官网下载Jquery
  14. Win10在桌面上怎么添加此电脑、网络图标、回收站等图标?
  15. jmeter 压力测试各种值的意思
  16. render createElement JSX
  17. 【高分一号影像数据命名规则】
  18. 股票指标SMA EMA WMA...
  19. 2021宝坻一中高考成绩查询,天津2021高考成绩排名榜单,天津各高中高考成绩喜报...
  20. orb-slam3安装编译运行。opencv3.2 undefined reference to `cblas_zgemm vgg_generated_48.i ippicv_linux_20151

热门文章

  1. matlab 图像语义分割,笔记︱图像语义分割(FCN、CRF、MRF)、论文延伸(Pixel Objectness、)...
  2. pca百分比取多少比较好_母亲节给妈妈发多少红包比较好合适 母亲节红包吉利数字含义...
  3. 这个统一了238个机器学习模型R包的参考手册推荐给你
  4. CIRIquant:circRNA定量和剪接体转换识别
  5. RTX5 | 事件标志组01 - 创建事件标志组
  6. Linux学习之diction的编译与使用
  7. 【ES10(2019)】String 扩展 trimStart / trimEnd
  8. input框placeholder样式修改
  9. 基于java教学管理系统设计(含源文件)
  10. mysql 连接 iOS_iOS连接mysql数据库及基本操作