https://blog.csdn.net/weixin_44730681/article/details/113728895 TCP/IP 协议 三次握手需要自己了解一下

1 未连接队列 和 已连接队列

  • 在linux系统内核中维护了两个队列:syns queueaccept queue


服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理。

1.1 syns queue

  • syns queue : 保存一个SYN已经到达,但三次握手还没有完成的连接。
  • 用于保存半连接状态的请求
  • 其大小通过/proc/sys/net/ipv4/tcp_max_syn_backlog 指定,一般默认值是512。
  • 不过这个设置有效的前提是系统的syncookies功能被禁用。

互联网常见的TCP SYN FLOOD恶意DOS攻击方式就是建立大量的半连接状态的请求,然后丢弃,导致syns queue不能保存其它正常的请求。

1.2 accept queue

  • accept queue:保存三次握手已完成,内核正等待进程执行accept的调用的连接。
  • 用于保存全连接状态的请求,其大小通过/proc/sys/net/core/somaxconn指定。

在使用listen函数时,内核会根据传入的backlog参数与系统参数somaxconn,取二者的较小值。

  1. // backlog 指定了内核为此套接口排队的最大连接个数;
  2. // 对于给定的监听套接口,内核要维护两个队列: 未连接队列和已连接队列
  3. // backlog 的值即为未连接队列和已连接队列的和。
  4. listen(int socketfd,int backlog)

1.3 backlog

  • 如果未设置backlog或所设置的值小于1,Java将使用默认值50。
  • 如果accpet queue队列满了,server将发送一个ECONNREFUSED错误信息Connection refused到client。


2. Netty ChannelOption.SO_BACKLOG 配置

  • 在netty实现中,backlog默认通过 NetUtil.SOMAXCONN指定;
  • 也可以在服务器启动启动时,通过option方法自定义backlog的大小。
  1. // server 启动引导
  2. ServerBootstrap serverBootstrap = new ServerBootstrap();
  3. // 配置启动的参数
  4. serverBootstrap.group(bossGroup,workerGroup)
  5. // 设置非阻塞,用它来建立新accept的连接,用于构造ServerSocketChannel的工厂类
  6. .channel(NioServerSocketChannel.class)
  7. // 临时存放已完成三次握手的请求的队列的最大长度。
  8. // 如果未设置或所设置的值小于1,Java将使用默认值50。
  9. // 如果大于队列的最大长度,请求会被拒绝
  10. .option(ChannelOption.SO_BACKLOG,128)
  11. .childOption(ChannelOption.SO_KEEPALIVE,true)
  12. .handler(new ChannelInitializer<SocketChannel>() {
  13. @Override
  14. protected void initChannel(SocketChannel ch) throws Exception {
  15. }
  16. });

3. TCP的连接状态

  1. SYN 表示建立连接
  2. FIN 表示关闭连接
  3. ACK 表示响应
  4. PSH 表示有 DATA数据传输
  5. RST 表示连接重置。

3.1 三次握手

image.png

  1. 第一次握手: 客户端向服务器端发送SYN标志的包建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

  2. 第二次握手: 服务器端,向客户端发送 SYN 和ACK的包 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态

  3. 第三次握手: 客户端向服务器端,收到服务端发送的SYN和ACK包,确认正确后,给服务器发送发送ACK的包, 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。完成三次握手,客户端与服务器开始传送数据。