TCP连接管理(2)

文章目录

三次握手

TCP服务器里面
发起建立连接一般都是client

在这里插入图片描述

  1. server里面存在有大量的连接,该如何管理呢?

先描述在组织
有描述连接的结构体,里面填充的就是描述连接的各自属性,后以各种数据结构连接起来
双方维护连接是有成本的(时间+空间)

  1. 为什么是3次握手(不是说三次握手一定成功,只是以较大概率建立连接的过程)
  • 我们并不担心第一次第二次丢了:因为第一次,第二次都有应答,第三次没有应答,就有可能有丢失的风险
    两点:保证对方好着没,网络好着没
  • 确认双方主机是否健康
  • 验证全双工,三次握手,是我们能看到双方都能看到收发的最小次数!

对客户端来说:发送数据syn验证了自己能发送数据,收到syn+ack验证了自己能收到数据,同时发送成功
对服务端来说:客户端发送syn验证了自己能收数据,发送syn+ACK,得到客户端回应ack说明了自己能发送数据,没有第三次握手无法证明服务端有发送的能力

  • 一次不行的原因:每次发送syn,服务端都要建立一个连接结构来管理,如果发送海量的syn,很容易受到攻击
  • 两次不行的原因:第二次握手的时候,服务端认为建立成功了,可能这个报文客户端没收到,丢弃了,客户端发送大量的syn,服务器端还是会维护大量的健康连接,消耗维护资源(SYN洪水)
  • 三次握手建立成功,双方是等量的消耗资源,可以杜绝纯小白,个人的攻击
  1. 对于client来说三次握手怎么样算完成

因为3次握手没有响应,只要client把ack发送出去,client就认为它握手完成
一般而言双方的握手成功是有时间差的:server段认为收到才算成功

  1. 第三次ack丢失

client认为连接已经完成,server认为连接没有完成
client就会理所当然的给server段发送数据,server(认为链接都没建立好就发送数据)收到了这个报文,这个报文不是建立连接的报文,就会返回一个RST(证明,刚才建立的连接失败,client就会关闭连接)

  1. 三次握手是双方的操作系统自动完成的,用户层完全不参与

client->connect ->发起三次握手(操作系统自动完成)
server->accept,握手成功

四次挥手(以最小的成本协商达成连接关闭的认识)

  1. 断开连接是双方的事情,双方随时都有可能发生
  2. 分别发送FIN,双方分别应答,就是四次握手,一方发送fin就是把自己的发送缓冲区给关闭了
  3. 没有人以断开连接来发起攻击,因为这个是让服务器更轻松
  4. 断开连接的本质:双方达成连接都应该断开的共识,就是一个通知对方的机制

在这里插入图片描述

理解TIME_WAIT

主动断开连接的一方,就要进入TIME_WAIT状态

  1. TIME\_WAIT状态上,认为自己的连接已经释放了,因为它已近收发了四次报文,即使它已近结束了,但是它还不能释放自己的结构,还要维护一段时间,等待自己的ack发送成功
    
  2. 不accept它也会建立连接
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int main(int argc, char *argv[])
{
if (argc != 2)
{
cout << "port" << endl;
exit(1);
}
uint16_t port = atoi(argv[1]);
int sockfd = Sock::Socket();
Sock::Bind(sockfd, port);
Sock::Listen(sockfd);
while (1)
{
int newsock = Sock::Accept(sockfd);
cout<<newsock<<endl;
//这里我们让服务器先断开

}
return 0;
}

连接建立成功这里estabilsh,我们把服务器断开,服务器就进入了time_wait状态
在这里插入图片描述

在这里插入图片描述

一旦TIME_WAIT状态,服务是无法立即重启的

MSL(max segment life):报文最大传输时间,一个报文在网络里面最大的存活时间
在这里插入图片描述

为什么要有TIME_WAIT?

TIME_WAIT一般是等待2msl的时间长度

  1. 尽量保证历史发送的网络数据在网络中消散,因为如果直接断开的话,还有一些双方发送的数据,并没有被读取
  2. 2msl刚好保证了数据是一来一回的,历史上的数据更好就卡在了出口路由器上,
  3. 尽量的保证最后一个ACK被对方收到,没有消息的话,就认为已近ack发送成功,如果中途收到fin,说明我发送ack失败

** 为什么会bind error**

我们断开连接之后,在连接,因为之前的服务器一方处于time_wait方面,这个连接没有断开,端口依旧被占用着,
因为一个端口号只能被一个进程绑定

无法立即重启有什么危害

1s都是很大的数据量,服务器的端口和ip都是不能改变的,客户不认识,连不上,所以必须使用地址复用的选项

理解close_wait

调用一个close就是2次挥手
如果客户端调用close而服务器不调用close,那么服务器就会一直处于close_wait状态,客户端就会处于fin_wait2状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main(int argc, char *argv[])
{
if (argc != 2)
{
cout << "port" << endl;
exit(1);
}
uint16_t port = atoi(argv[1]);
int sockfd = Sock::Socket();
Sock::Setoptsocket(sockfd);
Sock::Bind(sockfd, port);
Sock::Listen(sockfd);
while (1)
{
int newsock = Sock::Accept(sockfd);
cout<<newsock<<endl;
//这里我们不调用closefd,服务器就会处于close_wait状态

}
return 0;
}

在这里插入图片描述

启示

  1. 一个fd被用完,千万不要忘记释放!
  2. fd是有限制的,会造成fd泄露,fd被占用变得越来越少

TCP连接管理(2)
http://example.com/2022/08/31/TCP连接管理(2)/
作者
Zevin
发布于
2022年8月31日
许可协议