了解tcp底层知识

作者: | 更新日期:

之前网上搜集了一些tcp的资料,现在消化一下.

本文首发于公众号:天空的代码世界,微信号:tiankonguse

tcp-state-diagram

背景

之前,遇到客户端因使用短连接导致连接服务失败的为问题, 后来通过配置系统参数解决了.
当时还写了一篇记录 TIME_WAIT 简单记录(一) .
今天再来记录一些tcp的知识吧.

目录

这篇记录主要有这些内容:

  • 创建连接
  • read数据
  • write数据
  • 拥塞控制

当然, 记录的都是概念上的东西, 有问题可以找我讨论.

创建连接

tcp-connect

tcp 使用三次握手建立连接:

  1. 客户端向server发送SYN包
  2. server回复SYN+ACK
  3. 客户端返回ACK包

这里要记录的是连接的过程中, 服务端存在两个队列来储存状态.

  • 半连接队列
    当server回复SYN+ACK包时, 会将SYN_RECV状态的连接保存到半连接队列中
    队列长度由net.ipv4.tcp_max_syn_backlog设置
  • accept队列
    当客户端回复ACK包时, 会将ESTABLISHED状态的连接移入accept队列,等待应用调用accept()
    队列长度为min(net.core.somaxconn, backlog), backlog 为 listen函数的第二个参数.

这两个队列也引出了两个问题.

  1. SYN flooding
    客户端大量的发送SYN包, 不回ACK, 这时将会把半连接队列填满, 从而会影响正常服务.
    SYN cookie可以解决这个问题.
    SYN cookie 是将连接信息编码在ISN(initial sequence number)中返回给客户端, 客户端返回ACK时,再还原连接信息即可.
    我们可以配置 net.ipv4.tcp_syncookies 来开启这个功能.
  2. 大量连接
    这个问题暂时保留.

read数据

tcp-read

linux里面使用sk_buff数据结构来描述packet.

  1. 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进行处理。
  2. kernel将这个packet传递给IP层进行处理。IP层需要将信息组装成为ip packet。
    如果ip packet是tcp的话那么放到socket backlog里面。如果socket backlog满了的话那么将ip packet丢弃。
    tcp layer从socket backlog里面取出tcp packet, 然后会回ACK确认.
  3. 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)
  1. RX errors:收包总的错误数
  2. RX dropped: 表示数据包已经进入了Ring Buffer,但是由于内存不够等系统原因,导致在拷贝到内存的过程中被丢弃。
  3. RX overruns: overruns意味着数据包没到Ring Buffer就被网卡物理层给丢弃了,而CPU无法及时的处理中断是造成Ring Buffer满的原因之一

当dropped数量持续增加,建议增大Ring Buffer,使用ethtool -G进行设置。

Input Packet Queue(数据包接收队列)

当接收数据包的速率大于内核TCP处理包的速率,数据包将会缓冲在TCP层之前的队列中。接收队列的长度由参数net.core.netdev_max_backlog设置。

write 数据

tcp-write

  1. application layer将数据copy到tcp send buffer里面,如果空间不够的话那么就会出现阻塞
  2. tcp layer等待tcp send buffer存在数据或者是需要做ack的时候,组装ip packet推送到IP layer
  3. IP layer从kernel memory申请sk_buffer,将ip data包装成为packet data,然后塞到qdisc里面。如果队列满的话那么就会出现阻塞,反馈到tcp layer阻塞
  4. 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数据类似.

流量控制

tcp-congestion-control

初始状态是slow start
cwnd(congestion window) 拥塞窗口,表示一次最多发送的数据包多少。
ssthresh(slow start threshold) 慢速启动阈值。
MSS(maximum segment size) 最大分节大小,和传输网络的MTU相关。

参考资料

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越