三次握手
TCP服务器里面
发起建立连接一般都是client
- server里面存在有大量的连接,该如何管理呢?
先描述在组织
有描述连接的结构体,里面填充的就是描述连接的各自属性,后以各种数据结构连接起来
双方维护连接是有成本的(时间+空间)
- 为什么是3次握手(不是说三次握手一定成功,只是以较大概率建立连接的过程)
- 我们并不担心第一次第二次丢了:因为第一次,第二次都有应答,第三次没有应答,就有可能有丢失的风险
两点:保证对方好着没,网络好着没- 确认双方主机是否健康
- 验证全双工,三次握手,是我们能看到双方都能看到收发的最小次数!
对客户端来说:发送数据syn验证了自己能发送数据,收到syn+ack验证了自己能收到数据,同时发送成功
对服务端来说:客户端发送syn验证了自己能收数据,发送syn+ACK,得到客户端回应ack说明了自己能发送数据,没有第三次握手无法证明服务端有发送的能力
- 一次不行的原因:每次发送syn,服务端都要建立一个连接结构来管理,如果发送海量的syn,很容易受到攻击
- 两次不行的原因:第二次握手的时候,服务端认为建立成功了,可能这个报文客户端没收到,丢弃了,客户端发送大量的syn,服务器端还是会维护大量的健康连接,消耗维护资源(SYN洪水)
- 三次握手建立成功,双方是等量的消耗资源,可以杜绝纯小白,个人的攻击
- 对于client来说三次握手怎么样算完成
因为3次握手没有响应,只要client把ack发送出去,client就认为它握手完成
一般而言双方的握手成功是有时间差的:server段认为收到才算成功
- 第三次ack丢失
client认为连接已经完成,server认为连接没有完成
client就会理所当然的给server段发送数据,server(认为链接都没建立好就发送数据)收到了这个报文,这个报文不是建立连接的报文,就会返回一个RST(证明,刚才建立的连接失败,client就会关闭连接)
- 三次握手是双方的操作系统自动完成的,用户层完全不参与
client->connect ->发起三次握手(操作系统自动完成)
server->accept,握手成功
四次挥手(以最小的成本协商达成连接关闭的认识)
- 断开连接是双方的事情,双方随时都有可能发生
- 分别发送FIN,双方分别应答,就是四次握手,一方发送fin就是把自己的发送缓冲区给关闭了
- 没有人以断开连接来发起攻击,因为这个是让服务器更轻松
- 断开连接的本质:双方达成连接都应该断开的共识,就是一个通知对方的机制
理解TIME_WAIT
主动断开连接的一方,就要进入TIME_WAIT状态
- TIME_WAIT状态上,认为自己的连接已经释放了,因为它已近收发了四次报文,即使它已近结束了,但是它还不能释放自己的结构,还要维护一段时间,等待自己的ack发送成功
- 不accept它也会建立连接
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的时间长度
- 尽量保证历史发送的网络数据在网络中消散,因为如果直接断开的话,还有一些双方发送的数据,并没有被读取
- 2msl刚好保证了数据是一来一回的,历史上的数据更好就卡在了出口路由器上,
- 尽量的保证最后一个ACK被对方收到,没有消息的话,就认为已近ack发送成功,如果中途收到fin,说明我发送ack失败
** 为什么会bind error**
我们断开连接之后,在连接,因为之前的服务器一方处于time_wait方面,这个连接没有断开,端口依旧被占用着,
因为一个端口号只能被一个进程绑定
无法立即重启有什么危害
1s都是很大的数据量,服务器的端口和ip都是不能改变的,客户不认识,连不上,所以必须使用地址复用的选项
理解close_wait
调用一个close就是2次挥手
如果客户端调用close,而服务器不调用close,那么服务器就会一直处于close_wait状态,客户端就会处于fin_wait2状态
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;
}
启示
- 一个fd被用完,千万不要忘记释放!
- fd是有限制的,会造成fd泄露,fd被占用变得越来越少