简介

Wireshark是一个网络抓包分析软件,当线上出现各种连接相关的问题,如连接不复用,大量CLOSE_WAIT时,可以方便的使用Wireshark抓包软件进行抓包分析

安装

Wirewark在windows系统上默认使用的是WinPcap来抓包的,只能看到经过网卡的流量,看不到访问localhost的流量,可先安装Npcap,安装Wirewark时再选择不安装WInPcap即可抓localhost的包

基本使用

window下,直接只用wireshark客户端进行抓包



Linux下,使用tcpdump产生pcap文件,再通过wireshark导入分析

tcpdump -s0 host 192.168.162.103 and port 9999 -w my.pcap

典型场景分析

服务端代码:

public class SocketServer {
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket();
server.bind(new InetSocketAddress((InetAddress)null, 9999));
Socket socket = server.accept();
// socket.setKeepAlive(true); 默认情况下不进行心跳检测
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
Runtime.getRuntime().addShutdownHook(new Thread(()-> {
try {
socket.close();
server.close();
} catch (IOException e) {
System.out.println("close exception" + e);
}
}));
while(true) {
byte[] bytes = new byte[1024];
int size = in.read(bytes);
System.out.println("server read " + new String(Arrays.copyOf(bytes, size)));
out.write("hello client".getBytes());
out.flush();
}
}
}

客户端代码:

public class SocketClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("192.168.162.105", 9999);
OutputStream out = socket.getOutputStream();
out.write("hello server".getBytes());
out.flush(); InputStream in = socket.getInputStream();
byte[] bytes = new byte[1024];
int size = in.read(bytes);
System.out.println("server read " + new String(Arrays.copyOf(bytes, size)));
// Thread.sleep(22 * 1000); 休眠一段时间使得服务端进行心跳检测
out.close();
in.close();
socket.close();
}
}

1 观察三次握手、四次挥手



其中四次挥手只有3个tcp分组原因:四次挥手的时候,两个方向的断开是独立的,每个方向发送一个FIN,对方回复一个ACK,但同时,TCP规定ACK可以捎带在其他数据包当中,所以你看到的主动断开连接一方本应收到的ACK,是被对方的FIN包捎带过来的,就变成了三个包。并不是所有的情况下都是这样,典型的一种情况是,主动断开的一方发送FIN之后,被动一方仍然有数据要继续发送,就会先ACK这个FIN,然后继续发送数据(在此过程中主动断开一方仍然会继续ACK这些数据),直到数据发送完毕之后再发送FIN并接收对方的ACK。

2 观察tcp心跳检测机制(放开注释)

tcp心跳服务端参数说明

2.1 模拟客户端一段时间不传输数据



服务器net.ipv4.tcp_keepalive_intvl = 10,以上代码客户端sleep了22s,因此服务端进行了2次心跳检测

2.2 模拟MySQL Client突然掉线,抓取Server端

  • 最后一次正常请求后10s,服务端开始发送心跳包
  • 心跳包间隔3秒,发送3次
  • 3次后,服务端关闭连接

3 线上CLOSE_WAIT问题

在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态,由此分析可知通常是被动关闭方代码问题。

线上应用程序引入连接池后,访问clickhouse出现CLOSE_WAIT

  • 可以看到客户端一直没有回复FIN,研究clickhouse客户端源码实现发现clickhouse底层基于HttpClient实现JDBC接口,由于Http服务端并不会永久保持连接,当服务端超过Keep-Alive时间后会主动关闭连接,而客户端使用连接池后,不会释放关闭连接,导致客户端CLOSE_WAIT
  • 因此通过增加服务端和客户端的http层Keep-Alive时间,可以缓解这个问题,但是并不能根本解决
  • 由于HttpClient本身可以支持多个连接,所以对一个Connection进行管理,即可支持连接池,后续舍弃了Druid连接池,自己进行了客户端JDBC封装

此问题中客户端没有关闭连接,发送FIN导致客户端处于CLOSE_WAIT状态,理论上服务端没有收到FIN,应该处于FIN_WAIT_2的状态,但实际观察发现服务端已经完全关闭了,查看TCP配置发现,FIN_WAIT_2可通过tcp_fin_timeout配置FIN_WAIT_2超时时间,一旦超时会直接进入CLOSED状态,而不经过TIME_WAIT

4 服务端TIME_WAIT

主动关闭TIME_WAIT,被动关闭CLOSE_WAIT

TIME_WAIT时间配置内核没有透出,如果要改需重新编译内核

查看内核源码发现,默认TIME_WAIT时间为60s

https://yq.aliyun.com/ziliao/256040

Java客户端Socket常用配置(进程级别配置)

  • socket.setKeepAlive(true);

    是否开启tcp心跳检测机制,默认不开启,开启后会根据OS tcp_keepalive_time、tcp_keepalive_intvl、tcp_keepalive_probes进行心跳检测
  • socket.setReuseAddress(true);

    允许复用处于TIME_WAIT的socket
  • socket.setTcpNoDelay(true);在默认情况下,客户端向服务器发送数据时,会根据数据包的大小决定是否立即发送。当数据包中的数据很少时,如只有1个字节,而数据包的头却有几十个字节(IP头+TCP头)时,系统会在发送之前先将较小的包合并到较大的包后,一起将数据发送出去。在发送下一个数据包时,系统会等待服务器对前一个数据包的响应,当收到服务器的响应后,再发送下一个数据包,这就是所谓的Nagle算法;在默认情况下,Nagle算法是开启的。

    这种算法虽然可以有效地改善网络传输的效率,但对于网络速度比较慢,而且对实现性的要求比较高的情况下(如游戏、Telnet等),使用这种方式传输数据会使得客户端有明显的停顿现象。因此,最好的解决方案就是需要Nagle算法时就使用它,不需要时就关闭它。而使用setTcpToDelay正好可以满足这个需求。当使用setTcpNoDelay(true)将Nagle算法关闭后,客户端每发送一次数据,无论数据包的大小都会将这些数据发送出
  • socket.setSoLinger(true, 0);

  • socket.setSoTimeout(soTimeout);

    配置inputstream一个阻塞read的超时时间

Linux常用TCP参数(OS级别配置)

  • net.ipv4.tcp_keepalive_time

    当keepalive起用的时候,TCP发送keepalive消息的频度,单位为秒,缺省是7200秒(即2小时)
  • net.ipv4.tcp_keepalive_intvl

    keepalive探测包的发送间隔
  • net.ipv4.tcp_keepalive_probes

    如果对方不予应答,探测包的发送次数
  • net.ipv4.tcp_timestamps

    为1表示开启TCP时间戳,用来计算往返时间RTT(Round-Trip Time)和防止序列号回绕
  • net.ipv4.tcp_tw_reuse

    为1表示允许将TIME-WAIT的句柄重新用于新的TCP连接
  • net.ipv4.tcp_tw_recycle

    为1表示开启TCP连接中TIME-WAIT的快速回收,NAT环境可能导致DROP掉SYN包(回复RST),不要轻易与net.ipv4.tcp_timestamps一起开启
  • net.ipv4.tcp_fin_timeout

    FIN_WAIT_2状态的超时时长
  • net.ipv4.tcp_syncookies

    为1时SYN Cookies,当SYN等待队列溢出时启用cookies来处理,可防范少量SYN攻击
  • net.ipv4.tcp_max_tw_buckets

    保持TIME_WAIT套接字的最大个数,超过这个数字TIME_WAIT套接字将立刻被清除并打印警告信息
  • net.ipv4.ip_local_port_range

    设定tcp客户端发起连接随机端口范围,默认32768,61000,这个配置限制了此机器访问外部机器的连接数目
  • net.ipv4.tcp_max_syn_backlog

    端口最大backlog内核限制,防止占用过大内核内存
  • net.ipv4.tcp_syn_retries

    对一个新建连接,内核要发送多少个SYN连接请求才决定放弃,不应该大于255
  • net.ipv4.tcp_retries1

    放弃回应一个TCP连接请求前﹐需要进行多少次重试,RFC规定最低的数值是3,这也是默认值
  • net.ipv4.tcp_retries2

    在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试,默认值为15
  • net.ipv4.tcp_synack_retries

    TCP三次握手的SYN/ACK阶段重试次数,缺省5
  • net.ipv4.tcp_max_orphans

    不属于任何进程(已经从进程上下文中删除)的sockets最大个数,超过这个值会被立即RESET,并同时显示警告信息
  • net.ipv4.tcp_orphan_retries

    孤儿sockets废弃前重试的次数,缺省值是7
  • net.ipv4.tcp_mem

    内核分配给TCP连接的内存,单位是page:

    第一个数字表示TCP使用的page少于此值时,内核不进行任何处理(干预),

    第二个数字表示TCP使用的page超过此值时,内核进入“memory pressure”压力模式,

    第三个数字表示TCP使用的page超过些值时,报“Out of socket memory”错误,TCP 连接将被拒绝
  • net.ipv4.tcp_rmem

    为每个TCP连接分配的读缓冲区内存大小,单位是byte
  • net.ipv4.tcp_wmem

    为每个TCP连接分配的写缓冲区内存大小,单位是byte:

    第一个数字表示,为TCP连接分配的最小内存,

    第二个数字表示,为TCP连接分配的缺省内存,

    第三个数字表示,为TCP连接分配的最大内存(net.core.wmem_max可覆盖该值)

参考文档:

https://www.cnblogs.com/wangjq19920210/p/8440824.htm

https://www.zhihu.com/question/55890292

https://yq.aliyun.com/articles/581106

http://elf8848.iteye.com/blog/1739598

https://segmentfault.com/a/1190000012345710

TIME_WAIT很好的文章:

https://jin-yang.github.io/post/network-tcpip-timewait.html

tcp_timestamps抓包分析文章:

http://www.bubuko.com/infodetail-1650846.html

Wireshark抓包与常见问题解决的更多相关文章

  1. 接口测试之——Charles抓包及常见问题解决(转载自https://www.jianshu.com/p/831c0114179f)

    简介 Charles其实是一款代理服务器,通过成为电脑或者浏览器的代理,然后截取请求和请求结果达到分析抓包的目的.该软件是用Java写的,能够在Windows,Mac,Linux上使用,安装Charl ...

  2. Wireshark抓包分析/TCP/Http/Https及代理IP的识别

    前言 坦白讲,没想好怎样的开头.辗转三年过去了.一切已经变化了许多,一切似乎从没有改变. 前段时间调研了一次代理相关的知识,简单整理一下分享之.如有错误,欢迎指正. 涉及 Proxy IP应用 原理/ ...

  3. Wireshark抓包工具使用教程以及常用抓包规则

    转载:http://fangxin.blog.51cto.com/1125131/735178 Wireshark是一个非常好用的抓包工具,当我们遇到一些和网络相关的问题时,可以通过这个工具进行分析, ...

  4. TCP协议基础知识及wireshark抓包分析实战

    TCP相关知识 应swoole长连接开发调研相关TCP知识并记录. 数据封包流程 如图,如果我需要发送一条数据给用户,实际的大小肯定是大于你发送的大小,在各个数据层都进行了数据的封包,以便你的数据能完 ...

  5. Wireshark抓包数据:理解与分析

    wireshark是一个非常好用的抓包工具,本文根据平时抓包经验,对之前wireshark抓包的一些常见知识点进行了整理. 有不当之处,欢迎指正 1.SYN,FIN会消耗一个序号,单独的ACK不消耗序 ...

  6. Mac OS X上使用Wireshark抓包

    Wireshark针对UNIX Like系统的GUI发行版界面采用的是X Window(1987年更改X版本到X11).Mac OS X在Mountain Lion之后放弃X11,取而代之的是开源的X ...

  7. MAC Wireshark抓包IOS

    网络抓包是个基础技能,对于网络协议的掌握有一定的要求.iOS上实现网络抓包可以用Charles(针对http和https),tcpdump(快速分析网络包),和Wireshare.之前写过一篇介绍tc ...

  8. Wireshark抓包,带你快速入门

    前言 关于抓包我们平时使用的最多的可能就是Chrome浏览器自带的Network面板了(浏览器上F12就会弹出来).另外还有一大部分人使用Fiddler,Fiddler也是一款非常优秀的抓包工具.但是 ...

  9. Http实战之Wireshark抓包分析

    Http实战之Wireshark抓包分析 Http相关的文章网上一搜一大把,所以笔者这一系列的文章不会只陈述一些概念,更多的是通过实战(抓包+代码实现)的方式来跟大家讨论Http协议中的各种细节,帮助 ...

随机推荐

  1. python unicode和string byte

    python unicode 和string那 开发过程中总是会碰到string, unicode, ASCII, 中文字符等编码的问题, 每次碰到都要现搜, 很是浪费时间, 于是这次狠下心, 一定要 ...

  2. java 心跳机制

    心跳机制:就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开. 心跳包 心跳包就是在客户端和服务器间定时通知对方自己状态的一个自己定 ...

  3. org.springframework.beans.factory.UnsatisfiedDependencyException异常

    注解配置不完整 如Service实现类没有加 * @Service * @Transactional

  4. matplotlib基础汇总_02

    设置plot的风格和样式 点和线的样式 颜色 参数color或c 五种定义颜色值的方式 别名 color='r' 合法的HTML颜色名 color = 'red' HTML十六进制字符串 color ...

  5. 将形如 5D, 30s 的字符串转为秒

    import sys def convert_to_seconds(time_str): # write code here if 's' in time_str: return float(time ...

  6. PHP strrev() 函数

    实例 反转字符串 "Hello World!": <?php高佣联盟 www.cgewang.comecho strrev("Hello World!") ...

  7. log4net用法实例

    内容转载自:https://www.cnblogs.com/youring2/archive/2011/04/27/2030424.html 1.引用log4net.dll 2.在AssemblyIn ...

  8. 31-关键字:final

    final:最终的 1.可以用来修饰:类.方法.变量 2.具体的: 2.1 final 用来修饰一个类:此类不能被其他类所继承. * 比如:String类.System类.StringBuffer类 ...

  9. 如何设置远程MongoDB!

    默认情况下V服务连接着本地mongoDB服务,如果想连接到其他mongoDB服务,请按如下设置: 方法一:通过控制台修改 进入控制台 http://x.x.x.x:xxxx/system/consol ...

  10. GIT pull 如何解决 fatal: refusing to merge unrelated histories

    在Github新建一个仓库,写了Readme.md,然后把本地一个已有内容的仓库上传. 先pull,因为两个仓库不同,发现refusing to merge unrelated histories,无 ...