了解tcp底层知识
作者:
| 更新日期:之前网上搜集了一些tcp的资料,现在消化一下.
本文首发于公众号:天空的代码世界,微信号:tiankonguse
背景
之前,遇到客户端因使用短连接导致连接服务失败的为问题, 后来通过配置系统参数解决了.
当时还写了一篇记录 TIME_WAIT 简单记录(一) .
今天再来记录一些tcp的知识吧.
目录
这篇记录主要有这些内容:
- 创建连接
- read数据
- write数据
- 拥塞控制
当然, 记录的都是概念上的东西, 有问题可以找我讨论.
创建连接
tcp 使用三次握手建立连接:
- 客户端向server发送SYN包
- server回复SYN+ACK
- 客户端返回ACK包
这里要记录的是连接的过程中, 服务端存在两个队列来储存状态.
- 半连接队列
当server回复SYN+ACK包时, 会将SYN_RECV状态的连接保存到半连接队列中
队列长度由net.ipv4.tcp_max_syn_backlog设置 - accept队列
当客户端回复ACK包时, 会将ESTABLISHED状态的连接移入accept队列,等待应用调用accept()
队列长度为min(net.core.somaxconn, backlog), backlog 为 listen函数的第二个参数.
这两个队列也引出了两个问题.
- SYN flooding
客户端大量的发送SYN包, 不回ACK, 这时将会把半连接队列填满, 从而会影响正常服务.
SYN cookie可以解决这个问题.
SYN cookie 是将连接信息编码在ISN(initial sequence number)中返回给客户端, 客户端返回ACK时,再还原连接信息即可.
我们可以配置 net.ipv4.tcp_syncookies 来开启这个功能. - 大量连接
这个问题暂时保留.
read数据
linux里面使用sk_buff数据结构来描述packet.
- NIC(network interface controller)检测到packet到达, 从Kernel Memory(sk_buffs)分配sk_buff数据结构,调用DMA Engine将packet放到sk_buff数据结构里面, Ring Buffer里储存的是指向sk_buff的描述符.
Ring Buffer的大小固定, 当满了的时候, 新来的packet会被丢弃.
当DMA Engine完成处理之后, NIC通过向CPU发起中断 通知kernel进行处理。 - kernel将这个packet传递给IP层进行处理。IP层需要将信息组装成为ip packet。
如果ip packet是tcp的话那么放到socket backlog里面。如果socket backlog满了的话那么将ip packet丢弃。
tcp layer从socket backlog里面取出tcp packet, 然后会回ACK确认. - tcp recv buffer交给application layer处理
这里面又涉及两个队列: Ring Buffer, Poll list, socket backlog.
Ring Buffer
Ring Buffer位于NIC和IP层之间,是一个典型的FIFO.
可以通过ifconfig观察接收和传输队列的运行状况:
tiankonguse:linux $ ifconfig wlan0
wlan0 Link encap:以太网 硬件地址 74:29:af:0f:eb:59
inet 地址:192.168.31.137 广播:192.168.31.255 掩码:255.255.255.0
inet6 地址: fe80::7629:afff:fe0f:eb59/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:159849 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:101308 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:1000
接收字节:137793975 (137.7 MB) 发送字节:19003028 (19.0 MB)
- RX errors:收包总的错误数
- RX dropped: 表示数据包已经进入了Ring Buffer,但是由于内存不够等系统原因,导致在拷贝到内存的过程中被丢弃。
- RX overruns: overruns意味着数据包没到Ring Buffer就被网卡物理层给丢弃了,而CPU无法及时的处理中断是造成Ring Buffer满的原因之一
当dropped数量持续增加,建议增大Ring Buffer,使用ethtool -G进行设置。
Input Packet Queue(数据包接收队列)
当接收数据包的速率大于内核TCP处理包的速率,数据包将会缓冲在TCP层之前的队列中。接收队列的长度由参数net.core.netdev_max_backlog设置。
write 数据
- application layer将数据copy到tcp send buffer里面,如果空间不够的话那么就会出现阻塞
- tcp layer等待tcp send buffer存在数据或者是需要做ack的时候,组装ip packet推送到IP layer
- IP layer从kernel memory申请sk_buffer,将ip data包装成为packet data,然后塞到qdisc里面。如果队列满的话那么就会出现阻塞,反馈到tcp layer阻塞
- NIC driver如果检测到qdisc有数据的话,调用NIC DMA Engine将packet发送出去。发送完成之后NIC向CPU发起中断释放packet data内存到Kernel Memory
send Buffer
send Buffer有关的参数如下
net.ipv4.tcp_wmem =
net.core.wmem_default
net.core.wmem_max
QDisc
QDisc(queueing discipline )位于IP层和网卡的ring buffer之间。
QDisc的队列长度由txqueuelen设置,和接收数据包的队列长度由内核参数net.core.netdev_max_backlog控制所不同,txqueuelen是和网卡关联,可以用ifconfig命令查看当前的大小(发送队列长度)
Ring Buffer
和read数据类似.
流量控制
初始状态是slow start
cwnd(congestion window) 拥塞窗口,表示一次最多发送的数据包多少。
ssthresh(slow start threshold) 慢速启动阈值。
MSS(maximum segment size) 最大分节大小,和传输网络的MTU相关。
参考资料
本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。