TCP全连接队列与tcpdump抓包
listen的第二个参数加1是全连接队列的节点个数,在服务器来不及进行accept的时候,底层允许用户继续三次握手,建立连接成功,但是不能建立太多,最大数是blacklog+1。

全连接队列
由于底层有大量连接,因此要对连接进行管理,连接本质是内核的一种数据结构(struct,毕竟底层都是C语言写的)。
accept本质是从accept_queue中拿取已经建立成功的连接,只要accept_queue有节点,说明底层有积压,上层已经很忙了。但是全连接队列并不代表server只能同时处理backlog+1个连接,只是因为上层太忙了,来不及处理而已。可以参考生产消费模型,accept_queue就是生产者。
全连接队列可以减少服务的闲置率,增加给用户提供服务的效率和体验,当然队列也不能太长,就像过年买年货排队,对面等太长时间会离开的,而队列太长,服务器会开出很大一段内存来给队列,这段空间就造成浪费(5s后人家离开了,但是这5s内人家占着这个空间)。
当accept成功,会创建socket对象,进程PCB的文件描述符表数组内会放一个指针,指向struct file,然后struct file里又放个struct socket指针,这样可以找到的这个套接字了。
这个结构体是网络socket入口。
struct socket {socket_state state; // 套接字状态(如 SS_CONNECTED)short type; // 套接字类型(如 SOCK_STREAM)unsigned long flags; // 标志位(如非阻塞模式)struct socket_wq __rcu *wq; // 等待队列struct file *file; // 关联的文件描述符struct sock *sk; // 指向底层协议控制的 struct sockconst struct proto_ops *ops; // 协议操作函数集(如 TCP/UDP)
};
struct proto_ops {int family;struct module *owner;int (*bind) (struct socket *sock, struct sockaddr *addr, int addr_len);int (*connect) (struct socket *sock, struct sockaddr *addr, int addr_len, int flags);int (*socketpair)(struct socket *sock1, struct socket *sock2);int (*accept) (struct socket *sock, struct socket *newsock, int flags, bool kern);/* 其他操作函数指针 */
};TCP底层还是要创建一个struct tcp_sock,表示连接,这个就是在accept_queue排队的数据结构。

这玩意就是C风格的多态,把上边获得的socket结构体内的struct sock *sk指针拿到,然后强转成struct tcp_sock*指针,就能访问上面结构体内的成员函数了。
因此struct socket也叫BSD socket,即通用socket接口。下面的tcp_socket叫inet_sock(网络套接字)。
listen成功就会创建tcp_sock,放入accept_queue,如果accept就会创建struct file,里面放着socket指针,成员sock指向tcp_sock的sock。
TCP缓冲区与sk_buff队列的关系
TCP缓冲区通常通过
sk_buff结构体组成的队列来管理数据。sk_buff是Linux内核中用于表示网络数据包的核心数据结构,负责存储和管理网络协议栈各层的数据。TCP缓冲区的实现方式
在TCP协议中,发送缓冲区和接收缓冲区均由
sk_buff队列实现:
- 发送缓冲区:存放应用层写入但尚未发送的数据,或已发送但未确认的数据,通过
sk_buff队列维护。- 接收缓冲区:存放已接收但尚未被应用层读取的数据,同样通过
sk_buff队列组织。每个TCP套接字(
struct sock)包含两个关键队列:
sk_write_queue:发送队列,管理待发送的sk_buff。sk_receive_queue:接收队列,管理已接收的sk_buff。sk_buff队列的操作
内核通过队列操作函数管理缓冲区:
skb_queue_head_init(&sk->sk_receive_queue); // 初始化接收队列 skb_queue_tail(&sk->sk_write_queue, skb); // 添加skb到发送队列尾部 struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); // 从接收队列取出skb缓冲区大小与动态调整
TCP缓冲区大小受系统参数和套接字选项控制:
net.ipv4.tcp_rmem:接收缓冲区大小范围(最小值/默认值/最大值)。net.ipv4.tcp_wmem:发送缓冲区大小范围。- 可通过
setsockopt()动态调整缓冲区大小。性能优化相关
内核通过以下机制优化TCP缓冲区管理:
- 分片合并:尽可能合并连续的
sk_buff以减少内存碎片。- 预分配:提前分配
sk_buff以降低实时分配的开销。- 零拷贝:在某些场景下避免数据在用户态和内核态间的复制。
tcpdump抓包
安装tcpdump
sudo apt-get update
sudo apt-get install tcpdump
捕获所有网络接口上的TCP报文
sudo tcpdump -i any tcp注意:-i any 指定捕获所有网络接口上的数据包,tcp 指定捕获 TCP 协议的数据 包。i 可以理解成为 interface 的意思
捕获特定网络接口上的TCP报文
sudo tcpdump -i eth0 tcp
捕获特定源或目的IP地址的TCP报文
sudo tcpdump src host 192.168.1.100 and tcp
sudo tcpdump dst host 192.168.1.200 and tcp
sudo tcpdump src host 192.168.1.100 and dst host 192.168.1.200 and tcp捕获特定端口的TCP报文
sudo tcpdump port 80 and tcp保存捕获的数据包到文件
sudo tcpdump -i eth0 port 80 -w data.pcappcap 后缀的文件通常与 PCAP(Packet Capture)文件格式相关,这是一 种用于捕获网络数据包的文件格式。
注意事项
• 使用 tcpdump 时,请确保你有足够的权限来捕获网络接口上的数据包。通常,你 需要以 root 用户身份运行 tcpdump。
• 使用 tcpdump 的时候,有些主机名会被云服务器解释成为随机的主机名,如果不想要,就用-n 选项
• 主机观察三次握手的第三次握手,不占序号
