🌈 个人主页:Zfox_
🔥 系列专栏:Linux
一 :🔥 TCP 相干实行明白
🦋 listen 的第二个参数
- LISTEN(2) Linux Programmer's Manual
- NAME
- listen - listen for connections on a socket
- SYNOPSIS
- #include <sys/types.h> /* See NOTES */
- #include <sys/socket.h>
- int listen(int sockfd, int backlog);
复制代码
- 基于封装的 TcpSocket 实现以下测试代码
- 对于服务器, listen 的第二个参数设置为 1, 而且不调用 accept
- 测试代码链接: https://gitee.com/zfox-f/linux/tree/master/112/PLUS-MEAL/TestBacklog
🌿 这里我们将 TcpServer.cpp 的 accept 代码表明掉
注意:此时我们的 backlog 为 1
- const static int default_backlog = 1;
复制代码 此时启动 2 个客户端同时毗连服务器, 用 netstat 检察服务器状态, 统统正常.
但是启动第 3 个客户端时, 发现服务器对于第 3 个毗连的状态存在题目了
🦋 三次握手过程
🤝 TCP创建毗连 的过程称为 三次握手,简单流程如下:
- 第一次握手 :客户端发送 SYN 标志,体现发起毗连哀求;
- 第二次握手 :服务器收到哀求后,发送 SYN+ACK ,体现继承哀求并同步;
- 第三次握手 :客户端确认毗连,发送 ACK。
三次握手完成后,毗连进入全毗连队列,但此时应用层大概尚未处置惩罚这个毗连哀求。
💻 客户端状态正常, 但是服务器端出现了 SYN_RECV 状态, 而不是 ESTABLISHED 状态
⚙️ 这是由于, Linux 内核协议栈为一个 tcp 连继承理使用两个队列:
- 半毗连队列(syn queue): 存储已发送 SYN 的毗连,但尚未完成三次握手的毗连。每当收到 SYN 哀求,服务器将这个毗连放入半毗连队列,直到三次握手完成或超时失败。(用来生存处于 SYN_SENT 和 SYN_RECV 状态的哀求)
- 全毗连队列(accpetd 队列): 三次握手完成后,毗连进入全毗连队列,等候被应用层(通常是通过 accept() 函数)处置惩罚。如果全毗连队列已满,新毗连将被扬弃。 (用来生存处于 ESTABLISHED 状态, 但是应用层没有调用 accept 取走的哀求)
🔗 而全毗连队列的长度会受到 listen 第二个参数的影响,全毗连队列满了的时间,就无法继承让当前毗连的状态进入 ESTABLISHED 状态了.
这个队列的长度通过上述实行可知, 是 listen 的第二个参数 + 1
🦋 总结
listen 的第二个参数的本质是当服务器压力很大大概来不及获取新毗连的时间,操纵体系在底层,在 tcp 层会为我们维护一个全毗连队列,这个队列会把新到来的毗连维护起来,当我们未来须要的时间再把新毗连获取上去,这个队列的最大长度叫做 backlog + 1
🦋 开端明白全毗连队列
💻 在操纵体系中有应用层,传输层,网络层… 在传输层中有一个吸收队列 accept_queue,创建毗连时就举行三次握手。操纵体系中用户访问的网站多种多样,而且会并发的运行,以是在操纵体系内部肯定是要通过数据布局来举行管理的!
毗连本质就是操纵体系内核中的一批数据布局!
在传输层中将这个数据布局放入队列中举行管理!应用层会调用 accept 获取毗连,传输层就会返回给一个文件形貌符供应用层使用,通过这个文件形貌符,应用层就可以举行通讯!这个队列就是全毗连队列!
当应用层非常忙,来不及 accept,那么全毗连队列中会挤压毗连,这个总数不能凌驾 backlog !这个并不代表服务端只能同时处置惩罚 backlog + 1 个毗连。全毗连队列中的毗连体现毗连乐成但来不及实时处置惩罚的毗连!
全毗连队列的本质就是生产消耗模子,应用层从此中获取资源,传输层向此中放入资源!这个队列包管了在应用层较忙时无法获取毗连时,可以先将一些毗连维护起来,等候应用层调用,如答应以大大提升服从,进步毗连吞吐量!淘汰服务端闲置率,增长给用户提供服务的服从和体验!
🦋 深入明白全毗连队列
当服务器启动时,本质上是启动一个进程,那么就会有对应的 task_struct。在这个布局体中都会有 struct files_struct!此中包罗文件形貌符表 struct file*fd_array[],每个元素都指向文件布局体 struct file。
🍺 当创建网络套接字时,会创建一个 struct socket 布局体!在内核中时如许一个布局:
🦈 可以看到 struct socket 布局体内部有一个 struct file 布局体,但是未来我们是想通过文件形貌符找到对应的套接字,然后举行读取数据。但是现在是 struct socket 布局体内部有一个 struct file 布局体,如果通过 struct file 布局体找到套接字呢?
🐑 在 struct file 布局体有一个指针 void* private_data, 这个指针指向 struct socket 布局体。如许两个布局体就接洽起来了!
🚪 struct socket 布局体是 网络 Socket 的入口,其内部还包罗一个 const struct proto_ops 布局体
📐 这是一个方法集,聚集了 bind,connect… 一系列的函数指针!
固然我们 struct socket 布局体是内核中的套接字布局,但创建毗连时真实的数据布局是 struct socket 布局体中 struct sock *sk 所指向的 tcp_sock 布局体!
🛜 这是 TCP 套接字,此中包罗了慢启动算法阈值,拥塞窗口巨细,关联进程… 一系列 TCP 协议中的对应字段!这个 tcp_sock 就是三次握手时间创建的布局体!此中的第一个成员 struct inet_connection_sock 是复制毗连属性的! 这里就包罗毗连的相干信息。全毗连队列就在这个布局体中!
这里有超时重传的触发时间,TCP 毗连的状态,握手失败重试次数,全毗连队列…等数据。
struct inet_connection_sock 中的第一个成员是 struct inet_sock 布局体,这是网络层的布局体。
🏡 struct inet_sock 布局体此中包罗了 目的端标语,源端标语,目的 IP 地点 和 源 IP 地点 等数据!更紧张的是此中第一个成员是 struct sock 布局体,内里包罗着报文的一些属性 。这是整个 tcp_sock 中最底层的布局体,此中有两个字段:吸收队列 和 发送队列
- struct sk_buff_head sk_receive_queue;
- struct sk_buff_head sk_write_queue;
复制代码 这两个队列对于网络通讯至关紧张,由于它们直接加入了数据的吸收和发送过程。本日不详细讲授。
我们再回过来看 struct socket,此中有一个布局体指针 struct sock* sk,这个指针可以指向 tcp_sock 中最底层的 struct sock 布局体,然后可以通过范例转换,终极读取到整个 tcp_sock 布局体!也就是说,这个指针指向了 tcp_sock 布局体!
- 通过逼迫范例转换 这个指针可以直接指向 struct sock 、struct inet_sock 、 struct ine_connection_sock 访问对应的数据
- 通过布局体嵌套的方式,使用公共指针指向布局体头部对象的方式 这就是 C风格的多态! 此时 struct sock 就是基类!
🪣 同样的创建 UDP 套接字时,udp_sock 的第一个成员是 struct inet_sock 布局体(由于 udp 不须要毗连以是没有包罗毗连属性布局体)。那么终极也是一个 struct sock 布局体,以是也可以通过C风格的多态实现!
通过 基类 struct socket,我们可以举行 tcp 和 udp 的通讯,以是说他是网络 socket 的入口。
二 :🔥 全毗连队列满的优化
💻 在服务器全毗连队列满时,我们可以采取以下步调举行优化:
🦋 增大全毗连队列巨细
🍝 在 Linux 体系中,全毗连队列的巨细由 /proc/sys/net/core/somaxconn 控制,可以通过调解该值增大队列容量:
- echo 1024 > /proc/sys/net/core/somaxconn
复制代码 此下令将全毗连队列的巨细增大至 1024。同时,还需在应用步调中设置适当的 backlog 参数,比方在使用 listen() 函数时指定更大的值:
🦋 使用 SYN Cookie
🤩 当全毗连队列满时,Linux 内核可以启用 SYN Cookie 技能,克制拒绝新毗连。SYN Cookie 通过不将毗连放入全毗连队列,而是将状态信息嵌入 SYN-ACK 中的序列号中,比及客户端相应 ACK 时,再规复毗连状态。
启用 SYN Cookie:
- echo 1 > /proc/sys/net/ipv4/tcp_syncookies
复制代码 SYN Cookie 是一种有效的防御 SYN Flood 攻击的本领,同时能在高并发场景下提升毗连处置惩罚本领。
三 :🔥 使用 TCP dump 举行抓包, 分析 TCP 过程
本身测试的时间要注意哦, 注意和代码联合哦, 我们代码中故意在 close(sockfd) 那边留了一个题目
TCPDump 是一款强盛的网络分析工具, 紧张用于捕获和分析网络上传输的数据包。
🦋 安装 tcpdump
🎁 tcpdump 通常已经预装在大多数 Linux 发行版中。 如果没有安装, 可以使用包管理器举行安装。
- sudo apt-get update
- sudo apt-get install tcpdump
复制代码
- 在 Red Hat 或 CentOS 体系中, 可以使用以下下令:
🦋 常见使用
- 🍑 使用以下下令可以捕获全部网络接口上传输的 TCP 报文:
- $ sudo tcpdump -i any tcp
复制代码 注意: -i any 指定捕获全部网络接口上的数据包, tcp 指定捕获 TCP 协议的数据包。 i 可以明白成为 interface 的意思
- 🍑 如果你只想捕获某个特定网络接口(如 eth0) 上的 TCP 报文, 可以使用以下下令:
- $ ifconfig
- eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
- inet 172.18.45.153 netmask 255.255.192.0 broadcast
- 172.18.63.255
- inet6 fe80::216:3eff:fe03:959b prefixlen 64 scopeid
- 0x20<link>
- ether 00:16:3e:03:95:9b txqueuelen 1000 (Ethernet)
- RX packets 34367847 bytes 9360264363 (9.3 GB)
- RX errors 0 dropped 0 overruns 0 frame 0
- TX packets 34274797 bytes 6954263329 (6.9 GB)
- TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
- $ sudo tcpdump -i eth0 tcp
复制代码
- 🍑 使用 host 关键字可以指定源或目的 IP 地点。 比方, 要捕获源 IP 地点为 192.168.1.100 的 TCP 报文, 可以使用以下下令:
- $ sudo tcpdump src host 192.168.1.100 and tcp
复制代码
- 🍑 要捕获目的 IP 地点为 192.168.1.200 的 TCP 报文, 可以使用以下下令:
- $ sudo tcpdump dst host 192.168.1.200 and tcp
复制代码
- 🍑 同时指定源和目的 IP 地点, 可以使用 and 关键字毗连两个条件:
- $ sudo tcpdump src host 192.168.1.100 and dst host 192.168.1.200 and tcp
复制代码
- 🍑 使用 port 关键字可以指定端标语。 比方, 要捕获端标语为 80 的 TCP 报文(通常是HTTP 哀求) , 可以使用以下下令:
- $ sudo tcpdump port 80 and tcp
复制代码
- 🍑 使用 -w 选项可以将捕获的数据包生存到文件中, 以便后续分析。 比方:
- $ sudo tcpdump -i eth0 port 80 -w data.pcap
复制代码 这将把捕获到的 HTTP 流量生存到名为 data.pcap 的文件中。
- 相识: pcap 后缀的文件通常与 PCAP(Packet Capture) 文件格式相干, 这是一种用于捕获网络数据包的文件格式
- 🍑 使用 -r 选项可以从文件中读取数据包举行分析。 比方:
这将读取 data.pcap 文件中的数据包并举行分析
注意事项
- 使用 tcpdump 时, 请确保你有富足的权限来捕获网络接口上的数据包。 通常, 你须要以 root 用户身份运行 tcpdump。
- 使用 tcpdump 的时间, 有些主机名会被云服务器表明成为随机的主机名, 如果不想要, 就用 -n 选项
- 主机观察三次握手的第三次握手, 不占序号
🦋 通过抓包验证三次握手和四次挥手的过程:
📦 此时须要用到 gitee 上的测试代码举行验证:
- 起首我们使用抓取源目的 ip 和 tcp 的方式 抓取客户端发来的 SYN 数据包(第一次握手) 此时只启动客户端:
- tcpdump -n src host ip地址 and tcp
复制代码
此时我们就抓取到了 Flags 为 S 的 SYN 报文
💻 此时再将服务端和客户端同时启动, 由于我是在同一台主机上做的测试,以是会重复,实际上圈出来的三个就是 SYN 、 SYN + ACK 、 ACK , 可以看到第二个 ACK 就是对第一个 SYN 简直认序号
第三次的 ACK 就主动置 1 了, 双方开始正常通讯
此时我们直接 CTRL + C 杀掉客户端 可以发现抓取到 FIN 标识位 和 ACK 但是为什么只有两次挥手呢?
🧱 我们有来由信任我们代码中是有 bug 的!! 很有大概没有关闭文件形貌符!!!
🍉 果然 我们忘记加上 close() 了;
此时由于客户端和服务器关闭毗连险些是同时的,此时就造成了捎带应答!!!
此时我们让服务器 sleep 1 秒再退出
此时就能看到标准的 四次挥手了!!!
四:🔥 共勉
以上就是我对 【Linux】TCP 全毗连队列与 tcpdump 抓包 的明白,以为这篇博客对你有资助的,可以点赞收藏关注支持一波~😉
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|