> ## Content Index
> Fetch the complete content index at: https://blog.hacking.pub/llms.txt
> Use this file to discover other available public pages before exploring further.

# 大量假established
- URL: https://blog.hacking.pub/2017/01/30/da-liang-jia-established/
- Published: 2017-01-30T07:28:15.000Z
- Updated: 2018-08-23T05:26:07.000Z
- Author: chainhelen
- Tags: tcp, established, 四次挥手, 三次握手

#### 问题

上次tomcat并发一直有个问题没有解决，就是直播中途有一段时间，广东这边部分地区不能访问. 而从日志上来看，那个时间段是有用户授权访问的. 而且负载均衡各项硬件资源都是正常的（CPU、内存、网络连接均占用不到20%，未达到瓶颈）. 虽然有怀疑是[dns解析问题](http://img.hacking.pub/%E5%A4%A7%E9%87%8F%E5%81%87established/%E5%85%B3%E4%BA%8E%E5%BE%AE%E5%95%86%E5%9F%8E%E8%AE%BF%E9%97%AE%E5%BC%82%E5%B8%B8%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A.doc?ref=blog.hacking.pub)，但是复现不出来了.

#### 活动过后测试

测试复现中出现大量错误 READ ECONNRESET、CONNECT ETIMEOUT

下载了原生的tomcat，只访问index.html，也出现同样的问题

#### 准备过程

服务器A 运行脚本，发送http请求到服务器B上的8081端口 ip地址为112.74.128.91  
服务器B 上跑着tomcat，监听8081端口 ip地址为120.24.180.89

两个ip地址都是公网地址

**服务器A**  
第一  
为了只对出问题的报文进行研究，我在发送http报文的url上添加一些参数.

```js
//main.js
"use strict" 

let http = require('http')

let m = 0;
let n = 0;

for(let i = 0;i < 4000;i++) {
    http.get('http://112.74.128.91:8081/?id='+ i, (data)  => {
        m++;
        console.log(data.statusCode + "\t" + i + "\t" + m);
    }).on('error', (e) => {
        n++;
        console.log("error " + e.message + "\t" + i + "\t" + n);
    });
}

```

```
node ./main.js > 1.log & 
tail -f 1.log

```

第二  
同时使用tcpdump监听报文

```
tcpdump -i eth1  host 112.74.128.91 -w 01.12.15.11.client.cap

```

第三  
在发包过程中，使用如下命令可以随时查看tcp连接情况

```
netstat -an | grep "112.74.128.91:8081" | awk '/^tcp/ {++y[$NF]} END {for(w in y) print w, y[w]}'

```

![netstat_look](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/netstat_look.png)

**服务器B**  
第一  
启动服务器

```
export JRE_HOME=/root/jdk1.7.0/jre
export JAVA_HOME=/root/jdk1.7.0/
./startup.sh

```

第二  
抓包

```
tcpdump -i eth1 port 8081 -w 01.12.15.11.server.cap

```

第三  
查看连接数

```
netstat -an | grep "112.74.128.91:8081" | awk '/^tcp/ {++y[$NF]} END {for(w in y) print w, y[w]}'

```

#### tcp连接状态趋于稳定

**服务器A**  
tcp连接情况  
![test_tcpcount.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/test_tcpcount.png)

抓包情况  
![test_packetcapture.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/test_packetcapture.png)

日志  
![test_log.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/test_log.png)

**服务器B**  
tcp连接情况  
![jtest_tcpcount.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/jtest_tcpcount.png)

抓包情况  
![jtest_packetcapture.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/jtest_packetcapture.png)

#### 分析

先在wireshark中写入下面的过滤条件，看比较正常的http请求流程

```
tcp.stream eq 9

```

![tcp_stream_eq.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/tcp_stream_eq.png)

![tcp_stream_tu1.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/tcp_stream_eq_tu1.png)

![tcp_stream_eq_tu2.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/tcp_stream_eq_tu2.png)

如上图  
首先，26，30，31三个流水号，是典型的三次握手  
接下来每一次发送报文，对面都会回复ack  
ack的值是怎么确认的呢？通常来说

1. 如果A给B单纯的只是发送ACK确认报文（就是报文中tcp flag只有ACK的标志位是1），并且假设ack=n，seq=m，那么B给A下次发送的报文中ack=m，seq=n
2. 如果A给B发送的报文中，tcp flag中SYN或者FIN标志位为1，那么B给A下次发送的报文中ack=m+1, seq=n
3. 如果tcp flag中PSH标志位为1，那么B给A下次的报文中ack=m+TCP\_LEN，seq=n

注意4910是4708的延续，tcp报文分片，http报文太大了，被分成两个部分  
那什么时候tcp的数据会被分片呢？  
**MSS是最大报文段长度的缩写，是TCP协议里面的一个概念。**  
**MTU是网络传输最大报文包。**

> 我们看到，如果不考虑尽量避免IP分片所带来的不好的话，MSS跟MTU是没有什么关系的。MTU是传输能力，就像是“这条马路最大能够曾受5t的压力”。MSS是通信终端的应用数据处理能力，就像是“我这个工作间最大能够加工100t的雕像”。  
> MSS为1460是由1500-20（IP头）-20（TCP头）计算出的。  
> 实际场景下，TCP包头中会带有12字节的选项----时间戳。  
> 这样，单个TCP包实际传输的最大量就缩减为1448字节。1448=1500-20（IP头）-32（20字节TCP头和12字节TCP选项时间戳）

![](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/tcpsegmentformat.png)

紧接着，新的问题又出现了，可以看到4908序号的报文大小是4410字节，但是两端tcp协商的MSS是1460字节，怎么会超过1460字节大小  
得看这篇文章[wireshark抓到tcp包大于mss的包](http://blog.sina.com.cn/s/blog%5F93b45b0f0101pfwp.html?ref=blog.hacking.pub)  
盗图如下：  
![CapGtMss.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87establishedCapGtMss.png)  
![CapLeMss.png](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/CapLeMss.png)

#### 结论

结果很明显是因为下图中报文5238的ack丢失，导致client重新发送三次握手中的最后一次ack，后面server就不停地发送http回复报文的第一部分，客服端这时候已经傻眼了  
![result.PNG](https://blog.hacking.pub/qiniu/%E5%A4%A7%E9%87%8F%E5%81%87established/result.PNG)

[上述的抓包下载](http://img.hacking.pub/%E5%A4%A7%E9%87%8F%E5%81%87established/0130.14.16.cap.7z?ref=blog.hacking.pub)

### 参考

1. [理解TCP序列号（Sequence Number）和确认号（Acknowledgment Number）](http://blog.csdn.net/a19881029/article/details/38091243?ref=blog.hacking.pub)
2. [简析TCP的三次握手与四次分手](http://www.jellythink.com/archives/705?ref=blog.hacking.pub)
3. [TCP之深入浅出send和recv ](http://blog.csdn.net/olansefengye1/article/details/52027240?ref=blog.hacking.pub)
4. [Wireshark抓包TCP报长度比MSS大 && MSS 与MTU关系](http://blog.sina.com.cn/s/blog%5F93b45b0f0101pfwp.html?ref=blog.hacking.pub)
5. [wireshark抓到tcp包大于mss的包](http://blog.sina.com.cn/s/blog%5F93b45b0f0101pfwp.html?ref=blog.hacking.pub)
6. [一站式学习Wireshark（四）：网络性能排查之TCP重传与重复ACK](http://blog.jobbole.com/71427/?ref=blog.hacking.pub)
7. [关于wireshark抓包的那点事儿](http://lihongweibj.blog.51cto.com/6235038/1690518?ref=blog.hacking.pub)