言成言成啊 | Kit Chen's Blog

客户端连接已建立,但服务端却说没连上?一次TCP三次握手背后的真相

发布于2025-05-31 00:18:09,更新于2025-05-31 21:44:55,标签:java devops tcp  文章会持续修订,转载请注明来源地址:https://meethigher.top/blog

该问题是偶然发现的,起源于内网穿透在高并发时,DataProxyServer返回给User数据时,偶现数据丢失。 · Issue #8 · meethigher/tcp-reverse-proxy

复现步骤

  1. 开启TunnelServer
  2. 开启TunnelClient
  3. 开启自定义BackendServer。简单实现一个长连接TcpServer,连接进来之后,返回一段字符串即可。

BackendServer源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetServer;

public class BackendServer {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
NetServer netServer = vertx.createNetServer();
netServer.connectHandler(socket -> {
socket.remoteAddress();
socket.write(Buffer.buffer("SSH-2.0-OpenSSH_8.7")).onComplete(ar -> {
System.out.println(socket.remoteAddress().toString() + " write " + ar.succeeded());
});
}).listen(23).onComplete(ar -> {
if (ar.succeeded()) {
System.out.println("Server started on port " + ar.result().actualPort());
} else {
System.err.println("Server failed to start");
ar.cause().printStackTrace();
System.exit(1);
}
});
}
}

客户端与TunnelServer建立2000个长连接,会出现一种情况:客户端显示连接已经处于ESTABLISHED,但是服务端并没有该连接。

那简单,tcp抓包。发现客户端和服务端的三次握手均成功了。

那么,就得去了解三次握手建立连接的这个过程了。

对于客户端来说,当三次握手后,连接就会进入ESTABLISHED。

对于服务端来说,当三次握手后,操作系统内核会将连接放入到全连接队列AcceptQueue,此时连接会进入ESTABLISHED。若放入失败,则丢弃该连接或者发送RST。如果accept()过慢、连接建立过快。就会出现连接丢失的问题。

解决办法如下

  1. 调大AcceptQueue的参数值。临时立即生效sysctl -w net.core.somaxconn=5000,重启会失效。
  2. 设置若超过AcceptQueue最大值,则向客户端发送RST中止连接。临时立即生效sysctl -w net.ipv4.tcp_abort_on_overflow=1,重启会失效。

但是第二个方法有点鸡肋,即使AcceptQueue满了,也不一定会触发。因此保险起见,还是调大AcceptQueue比较好。

发布:2025-05-31 00:18:09
修改:2025-05-31 21:44:55
链接:https://meethigher.top/blog/2025/tcp-established-but-not-visible/
标签:java devops tcp 
付款码 打赏 分享
Shift+Ctrl+1 可控制工具栏