端口复用

使用场景

我们知道主动关闭连接方,最终会进入一个状态——timewait, 而当服务器主动关闭的时候,它会进入这个状态并等待2MSL的时长。

假设一个场景,当服务器需要更新时, 需要服务器主动关闭连接,这时候服务器就需要等待2msl才能使用这个端口,对于用户而言,2msl的时长太长,(linux的2msl时长大约有40s到60s),所以需要复用这个端口,使得服务端能够正常运行。

函数简介

#inlude<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(级别)指定系统中解释选项的代码为通用套接字代码,或为某个特定协议的代码。

  • optval是一个指向某个变量(*optval)的指针,setsockopt从*optval中取得选项待设置的新值,getsockopt则把已获得的选项当前值存放到*optval中。

对于端口复用的实现, 需要设置level,optname

  • level(级别) 选为SOL_SOCKET

  • optname设为SO_REUSEADDR(允许重用本地地址)或者SO_REUSEPORT(允许重用本地端口)

其中opt_val = 1表示启用,为0表示禁用。

实现例子

注意:这个是基于多线程实现的服务器端

server.cpp

#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<fcntl.h>
#include<ctype.h>
#include<pthread.h>
#include<string.h>
#include<stdio.h>#include"wrap.h"const int  MAXVALUE = 8192;
const int  SERV_PROT = 8888;//定义一个结构体将客户端地址和套接字绑定起来
struct s_info{struct sockaddr_in cliaddr;int connfd;
};void* do_work(void *arg){                             //子线程int n,i;struct s_info *ts = (struct s_info*)arg;char buf[MAXVALUE];char str[INET_ADDRSTRLEN];                          //define Inet_addrstrlen 16 while(1){n  = Read(ts->connfd, buf, MAXVALUE);if(n == 0){printf("the client %d closed...\n", ts->connfd);break;             }printf("recieved from %s at port %d\n", inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)), ntohs((*ts).cliaddr.sin_port));                 //note inet_ntop 网络字节序转换为ip地址,返回的是str的地址,for(i = 0;i<n;i++){buf[i] = toupper(buf[i]); }Write(STDOUT_FILENO, buf, n);Write(ts->connfd, buf, n);}Close(ts->connfd);return (void*)0;//pthread_exit(0);
}int main(){struct sockaddr_in seraddr, cliaddr;socklen_t cliaddr_len;int listenfd, connfd;pthread_t tid;struct s_info ts[256];int i=0;//创建套接字listenfd = Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用 int opt = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt));//设置服务器地址结构体bzero(&seraddr, sizeof(seraddr));seraddr.sin_family = AF_INET;seraddr.sin_addr.s_addr = htonl(INADDR_ANY);seraddr.sin_port = htons(SERV_PROT);//绑定套接字Bind(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr));//监听套接字Listen(listenfd, 128);printf("Accepting client connect ......");while(1){cliaddr_len = sizeof(cliaddr);connfd = Accept(listenfd, (struct sockaddr*)&cliaddr, &cliaddr_len);    //阻塞监听客户端请求ts[i].cliaddr = cliaddr;ts[i].connfd = connfd;//创建子线程pthread_create(&tid, NULL, do_work, (void*)&ts[i]); pthread_detach(tid);                                                    //子线程分离,防止僵尸线程i++;}return 0;
}

实践

第一步

将当前目录下的server.cpp编译并启动

g++ server.cpp -o server -pthread./server

第二步

开启另一个终端,并使用nc作为客户端与服务端连接 格式 nc 127.0.0.1 端口号

nc 127.0.0.1 8888

第三步

先将服务端关了,再将客户端关了,模拟服务端变成timewait状态,因为进入了timewait状态需要等待2msl时长才会真正close,而端口复用允许它复用8888这个端口和客户端通信。

为了验证一下,我们用netstat来查看8888这个端口的状态

sudo netstat -apn | grep 8888output:tcp        0      0 127.0.0.1:8888          127.0.0.1:47780         TIME_WAIT

第四步

重新开服务器

./server

并在另一个shell查看

sudo netstat -apn| grep 8888tcp        0      0 0.0.0.0:8888            0.0.0.0:*               LISTEN      5346/./mult_pthread
tcp        0      0 127.0.0.1:8888          127.0.0.1:47780         TIME_WAIT   -

发现

这里的端口复用并不是将time_wait提前结束了,为了保证tcp的可靠性,time_wait依然存在,只不过是重新利用这个端口进行另一个通信罢了。

发现8888有两个状态 listen 和time_wait, 如果把setsockopt给关掉了,又会怎样?

使用setsockopt实现端口复用相关推荐

  1. C/C++端口复用SO_REUSEADDR(setsockopt参数)

    端口复用最常用的用途应该是防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口.这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口.如果没有设定端口复用,绑定会失败 ...

  2. linux socket 端口复用 SO_REUSEADDR

    下面建立的套接字都是tcp套接字 1.进程创建监听套接字socket1,邦定一个指定端口,并接受了若干连接.那么进程创建另外一个套接口socket2,并试图邦定同一个端口时候,bind错误返回&quo ...

  3. 端口复用和半关闭补充

    端口复用: int opt=1 : //设置端口复用 setsockopt(lfd,SOL_SOCKET,SO_REUSERADDR,(void *)&opt,sizeof(opt)); 半关 ...

  4. 端口复用突破防火墙(图)

    如何在溢出后得到安全的.隐蔽的Shell是大家一直都在讨论的问题,因为现在的防火墙和各种安全软件漫天飞,想不被它们发现还真是很难,幸好有很多牛人们用自己的实力探索出了一条这样的道路,让我们这些人能顺着 ...

  5. linux网路编程之TCP状态转换及端口复用

    (1)TCP状态转换图 其中图中分为三种状态:实线代表的主动发起连接,虚线代表的被动发起连接,细实线代表的可以双向发起连接的状态. 主动发起连接方状态变化:1)主动发起连接的一方发送SYN标志位,进入 ...

  6. 网络基础4(TCP三次握手,四次握手,TCP流量控制,TCP状态转换 , TCP异常断开,设置TCP属性,端口复用)

    TCP协议 TCP通信时序 下图是一次TCP通讯的时序图.TCP连接建立断开.包含大家熟知的三次握手和四次握手. TCP通讯时序 在这个例子中,首先客户端主动发起连接.发送请求,然后服务器端响应请求, ...

  7. 安全之路 —— 利用端口复用技术隐藏后门端口

    简介 前面我们介绍到我们可以用进程注入的方法,借用其他应用的端口收发信息,从而达到穿墙的效果,那么今天介绍一种新的方法,叫做端口复用技术,他能够与其他应用绑定同一个端口,但同时进行端口复用的程序会接管 ...

  8. php开启端口复用,WebServer端口复用后门

    0x00 有朋友问到了我一个关于"无端口可用"的问题.说在如下图所示的内网环境中,firewall只允许Web Server的80端口建立网络连接,并且Web Server上的80 ...

  9. 关于Socket中端口复用(udp)

    最近在处理单位的外场遇到了一个实际问题. 在实际与第三方系统进行通信的过程中,我绑定了30009端口作为UDP服务端,但是偶尔会发现第三方系统向30009端口发送数据的时候,通过网络抓包可以抓到,但是 ...

最新文章

  1. Ceilometer Polling Performance Improvement
  2. 禅道设置bug模板_JPress v3.0 beta.2 发布,修复 bug 和完善产品细节
  3. JConsole/JvisualVM 远程连接失败处理
  4. H01-P1201-0.6B金升阳高压模块
  5. 给定一个年份,判断这一年是不是闰年。
  6. 【矩阵乘法】生成树计数(luogu 2109/NOI 2007)
  7. luogu P2516 [HAOI2010]最长公共子序列
  8. Symantec改变了产品下载方式
  9. javascript字典中添加数组_如何在 JavaScript 中更好地使用数组
  10. 在CentOS6.5上安装Tomcat6
  11. mysql 多个表union查询_mysql查询两个表,UNION和where子句
  12. tiledmap 图块属性_TiledMap地图使用
  13. 图论(5)邻接谱,邻接代数,图空间,托兰定理
  14. 批处理删除指定文件或文件夹
  15. ETC是什么,ETC系统主要有哪几部分构成?
  16. LeetCode. 15 - 三数之和
  17. 8. python基础之基础数据类型--bytes
  18. 10019---CSS Grouping Selectors(分组和嵌套)
  19. 数据库05子查询,union
  20. pytorch BatchNorm参数详解,计算过程

热门文章

  1. html escape函数,Javascript escape() 函数和unescape() 函数
  2. linux下的can驱动测试,linux系统下can的驱动测试
  3. 【信号去噪】基于奇异值分解(SVD)实现数字信号降噪含Matlab源码
  4. Arithmetic Sharing(算术共享)
  5. 用objectARX实现了一个复杂实体的Jig
  6. C语言实现乘方运算(m的n次方)
  7. Kotlin - 元组 Pair、Triple
  8. 安装X86平台的SylixOS操作系统
  9. 史上最全交互设计原则(二)之菲茨定律和米勒定律
  10. 简洁开源导航主题—酷啦鱼主题1.0.0版+WP内核