TCP握手中的半连接队列和全连接队列
日期: 2018-06-26 分类: 个人收藏 322次阅读
在之前学习TCP的三次握手时发现了一个比较重要的知识点,backlog。但是我没有遇到过。此篇权当是记录知识点以备后续。
流程图:
在握手阶段存在两个队列:syns queue(半连接队列);accept queue(全连接队列)。
流程简述如下:
1. 客户端发送SYN付服务端进行第一次报文握手,此时服务端将此请求信息放在半连接队列中并回复SYN+ACK给客户端。
此处就是SYN Flood(后续关注,此篇记录)攻击的点,攻击方要做的就是不停的建立连接,但是确不给出ACK确认,导致半连接队列满了,其他请求无法进入。
2. 客户端收到SYN+ACK,随机发出ACK确认给服务端;
全队列未满:从半连接队列拿出此消息放入全队列中。
全队列已满:处理方式和tcp_abort_on_overflow(cat /proc/sys/net/ipv4/tcp_abort_on_overflow)有关:
tcp_abort_on_overflow=0;表示丢弃该ACK;
tcp_abort_on_overflow=1;表示发送一个RST给客户端,直接废弃掉这个握手过程。
3. 服务端accept处理此请求,从全队列中将此请求信息拿出。
backlog:
backlog表明它是已连接但是还未处理的队列的大小。这个队列如果满的话,会发送一个ECONNREFUSED错误信息给客户端。就是常见的“Connection Refused”。
简单测试:使用Java内的socket测试:
客户端:
private static Socket[] clients = new Socket[30];
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
clients[i] = new Socket("127.0.0.1", 8888);
System.out.println("Client:" + i);
}
}
服务端1:
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(8888, 5);
while (true) {
// server.accept();
}
}
输出:
Client:0
Client:1
Client:2
Client:3
Client:4
Exception in thread "main" java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at java.net.Socket.connect(Socket.java:538)
at java.net.Socket.<init>(Socket.java:434)
at java.net.Socket.<init>(Socket.java:211)
at Test1.main(Test1.java:15)
源码简析:从下面的源码内可以看见backlog提供了一个类似容量的限制,小于1或者不传默认值是50。因此在只提供了5个backlog大小的测试中,因为前5个占据了全连接队列没有被处理,第六个进来时,没有位置,所以拒绝了(从此处可以看见参数tcp_abort_on_overflow=1,0的话会直接丢弃)
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
setImpl();
if (port < 0 || port > 0xFFFF)
throw new IllegalArgumentException(
"Port value out of range: " + port);
if (backlog < 1)
backlog = 50;
try {
bind(new InetSocketAddress(bindAddr, port), backlog);
} catch(SecurityException e) {
close();
throw e;
} catch(IOException e) {
close();
throw e;
}
}
服务端2:(注释打开)
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(8888, 5);
while (true) {
server.accept();
}
}
输出:
Client:0
Client:1
Client:2
Client:3
Client:4
Client:5
Client:6
Client:7
Client:8
Client:9
此时由于accept处理的存在,使得全连接队列有位置,所以能够处理完所有的请求。
全连接队列的大小:
Java的Socket的默认backlog大小是50;(看上面源码分析)
Tomcat的默认backlog大小是100;(看8080端口)
可以自由配置
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxThreads="800" acceptCount="1000"/>
AliTomcat默认backlog值为200;(参考别的连接)
Nginx的默认backlog值是511;
全队列的大小和backlog有关,但是未必是backlog的值,是由backlog和somaxconn(系统参数)两个值中的较小值决定。
cat /proc/sys/net/core/somaxconn
此处我贴一下linux下的大小是128,如果我们传进来的值是50,那么全队列大小就是50。
半连接队列的大小:
半连接的大小取决于64和tcp_max_syn_backlog的较大值。
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
本机的值是256。
通过ss查看Socket的状态:
基于上面的测试例子中的Java代码,这次backlo的值设置为10,作为明显区分。
Send-Q表示的就是当前端口的全连接队列大小,Recv-Q表示的是全连接队列使用的多少。
针对8888端口:客户端只循环了5次,所以Recv-Q的使用量只有5个,而全连接队列的大小是10。
先记录这么多,因为没有什么实际的问题作为继续研究的样例,所以没什么再深入的学习了。
贴一下参考博客:
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
标签:web相关
精华推荐