Linux 内核源码分析-TCP 协议-1
概述
TCP协议是整个Linux内核中比较重要的部分之一,我们基于Linux目前最稳定的版本4.17.13来对TCP协议进行比较系统的分析,读者可以参照博文来对源码进行分析学习,每个代码片段都附有在整个内核中的路径,方便大家查看。TCP实现复杂且庞大,我们会分几篇来介绍。
通常我们在进行网络编程的时候很少关注接口的具体实现,对于TCP连接的建立,关闭和阻塞控制等等也停留在具体的概念上,这里我们首先从接口开始,采用自顶向下的方法,从用户层面慢慢的深入了解TCP的实现。
我们通过分析源码具体要学习什么?
TCP代码实现
TCP参数优化
TCP网络分析工具
背景知识
TCP接口
我们通常在网络编程中使用以下几个接口:
socket()
:返回一个套接字bind()
:将套接字与指定的端口相连listen()
:监听socketconnect()
:连接服务器套接字accept()
:接受来自客户端的连接recv()
:读取数据send()
:发送数据close()
:关闭连接
socket的数据结构
通常我们在使用socket()
的接口来创建一个套接字时,需要指定具体的协议,比如AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)
,这里我们只来看一下IPv4的socket结构:
include/net/inet_sock.h
1 | /** struct inet_sock - representation of INET sockets |
这里定义了IPv4协议族的socket结构,每个字段有其注释方便大家理解,其中的sk
为通用的socket结构定义,与协议无关。
三次握手
这是被大家提及最多的三次握手,而其中的具体的实现我们从Client和Server慢慢来探究。
Client主动打开连接
一般情况下,TCP中Client主动建立连接的三个过程为:
- Client发送一个SYN段
- Client接受一个SYN段以及对Server端对SYN段的ACK
- Client对Server的回复ACK
第一次握手: Client发送SYN段
net/ipv4/tcp_ipv4.c
1 | /* This will initiate an outgoing connection. */ |
这里主要的函数是tcp_connect()
来构造SYN段并发送的过程,我们详细来看下这段代码:
1 | /* Build a SYN and send it off. */ |
关于第一次握手,我们梳理了最关键的SYN段的逻辑,其中SYN的seq序号我们可以得知与双方的地址、端口有关,通过hash计算得出。
第二次握手:接收SYN和ACK段
TCP是一个典型的状态机实现,根据不同的状态,状态机作出不同的回应:
net/ipv4/tcp_input.c
1 | /* |
tcp_rcv_synsent_state_process
是二次握手的核心处理逻辑,我们来梳理一下:
1 | /* 参数: |
第三次握手:发送ACK段
net/ipv4/tcp_output.c
__tcp_send_ack()
是发送ACK的具体实现:
1 | /* This routine sends an ack and also updates the window. */ |
这就是整个TCP三次握手中Client主动连接的实现部分,后面我们会从Server的角度来分析被动连接的实现。
主动打开的三次握手总结
- Client发送SYN段,SYN的
seq
与双方的地址、端口有关,完成后TCP状态为TCP_SYN_SENT
- Client收到Server的SYN和ACK段,对Server的ACK的
ack_seq
进行检查,完成后TCP状态为TCP_ESTABLISHED
- Client回复ACK段,其中Client的ACK的
ack_seq
为Server发送的TCP报文中的seq+1