1. TCP 协议简介
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议。它负责在不可靠的网络上提供可靠的数据传输。
TCP 协议的内核实现是操作系统中非常重要的一部分。本文将重点深入剖析 Linux TCP 内核实现的细节,帮助读者更好地理解 TCP 协议的工作原理。
2. TCP 内核模块
2.1 TCP 核心模块
Linux 内核中 TCP 实现的核心模块是位于文件 net/ipv4/tcp_ipv4.c
中的tcp_v4
模块。它负责处理所有与 TCP 相关的网络数据包,包括连接建立、数据传输、连接关闭等过程。
在该模块中,主要包含以下几个重要的函数:
tcp_v4_rcv
: 处理接收到的 TCP 数据包,并根据包的类型进行相应的处理。
tcp_v4_connect
: 建立 TCP 连接,包括三次握手过程。
tcp_v4_send_check
: 计算 TCP 数据包的校验和。
2.2 TCP Socket 模块
Linux TCP 内核实现中的另一个重要模块是tcp_sock
,位于文件 net/ipv4/tcp.c
中。该模块定义了 TCP 连接相关的数据结构和函数,包括 TCP Socket 的创建、连接管理、数据传输等功能。
在该模块中,主要包含以下几个关键函数:
tcp_create_openreq_child
: 创建一个 TCP Socket,并进行必要的初始化。
tcp_connect
: 处理主动发起的连接请求。
tcp_sendmsg
: 发送数据。
tcp_recvmsg
: 接收数据。
tcp_close
: 关闭 TCP 连接。
3. TCP 建立连接过程详解
3.1 SYN 表示连接请求
在 TCP 连接的建立过程中,客户端通过发送 SYN 数据包表示发起连接请求。Linux 内核中的 TCP 实现会在tcp_v4_rcv
函数中捕获到这个包,并调用 tcp_v4_connect
函数进行处理。
重要部分:
/* Handle incoming SYN packets for existing connections. */
static void tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{
...
/* Deal with the SYN only if our backlog counter can take it. */
if (sk_acceptq_is_full(sk)) {
refcnt_debug_release(&sk->sk_refcnt);
inet_csk_free_icsk(&sk->sk_inet);
sock_put(sk);
return;
}
...
}
上述代码片段展示了当接收到 SYN 包后,内核如何处理。其中sk_acceptq_is_full
函数用于判断接收队列是否已满。如果接收队列已满,连接请求将被拒绝。
3.2 TCP 三次握手过程
在 TCP 连接建立过程中,最重要的一步是三次握手。该过程涉及到客户端和服务端之间的互相确认,确保双方都可以正常通信。
在 Linux 内核实现中,tcp_v4_connect
函数负责处理三次握手过程:
static int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{
...
/* Try to find a source address */
err = inet_rcv_saddr(sk, &daddr, uaddr->sa_family);
...
/* Create a tcp_sock. */
child = tcp_create_openreq_child(sk, req, &err);
...
/* Send initial SYN. */
tcp_connect(struct sock *sk)
...
}
重要部分:
/* Sets up an initial connection request. */
int tcp_connect(struct sock *sk)
{
...
do {
req->num_retries++;
req->srtt_us -= nrto;
tcp_send_syn_data(sk, req);
} while (sk->sk_state != TCP_SYN_SENT);
...
}
上述代码片段展示了在tcp_v4_connect
函数中,如何发送 SYN 数据包进行三次握手的过程。其中的 tcp_send_syn_data
函数用于发送 SYN 数据包。
4. TCP 数据传输过程详解
4.1 数据发送
在 TCP 连接建立之后,数据的发送是非常重要的操作。Linux 内核中的tcp_sendmsg
函数负责处理数据发送的过程:
static int tcp_sendmsg(struct socket *sock, struct msghdr *msg, int size)
{
...
err = tcp_transmit_skb(sk, skb, flags, chunk, &err_val);
...
}
重要部分:
/* Send a data skb.
*/
static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
int flags, bool clone_it, int *err)
{
...
/* If queue is empty and can more be sent now? */
if (tcp_send_head(sk) && !after(tcp_sk(sk)->write_seq, tcp_sk(sk)->pushed_seq))
tcp_push(sk, tcp_sk(sk)->pushed_seq);
...
}
上述代码片段展示了tcp_sendmsg
函数中如何调用 tcp_transmit_skb
函数进行数据发送。
4.2 数据接收
在 TCP 数据传输过程中,数据的接收同样也是非常重要的。Linux 内核中的tcp_recvmsg
函数负责处理数据接收的过程:
static int tcp_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
int nonblock, int flags, int *addr_len)
{
...
/* Read data from receive buffer */
copied = tcp_read_sock(sk, msg, size, flags);
...
}
重要部分:
/* Read data from receive buffer and put it to msg. */
static int tcp_read_sock(struct sock *sk, struct msghdr *msg, size_t len,
int flags)
{
...
/* Pull data into process memory */
skb_splice_bits(&sk->sk_receive_queue, &iter, msg->msg_iter);
...
}
上述代码片段展示了tcp_recvmsg
函数中如何调用 tcp_read_sock
函数进行数据接收。
5. TCP 连接关闭过程详解
5.1 主动关闭连接
当主动关闭 TCP 连接时,Linux 内核中的tcp_close
函数负责处理关闭过程:
void tcp_close(struct sock *sk, long timeout)
{
...
/* Send FIN to the other end. */
tcp_send_active_reset(sk, sk->sk_shutdown == SHUTDOWN_MASK);
...
}
重要部分:
/* Send an active close reset packet.
*/
static void tcp_send_active_reset(struct sock *sk, bool apply_user_timeout)
{
...
/* Send RST */
tcp_send_reset(sk, TCP_RESET_CAUSE_USER, GFP_ATOMIC, TCPHDR_FIN | TCPHDR_ACK);
...
}
上述代码片段展示了tcp_close
函数中如何发送 FIN 数据包进行主动关闭的过程。其中的 tcp_send_active_reset
函数用于发送 RST 数据包。
5.2 被动关闭连接
当被动关闭 TCP 连接时,也需要进行相应的处理。在 Linux 内核中,tcp_v4_conn_request
函数中的以下代码段负责处理被动关闭连接的情况:
if (th->fin && !tcp_hdr(skb)->doff) {
if (!sock_flag(sk, SOCK_DEAD)) {
...
}
inet_csk_destroy_sock(sk);
goto discard;
}
上述代码片段展示了当接收到 FIN 数据包后的处理过程。当接收到 FIN 数据包时,内核会判断连接是否处于活动状态,如果是则调用 inet_csk_destroy_sock
函数进行连接关闭。
6. 总结
本文深入剖析了 Linux TCP 内核实现的细节,包括核心模块和 Socket 模块的功能及相关函数。同时,重点解释了 TCP 连接建立、数据传输和连接关闭的过程。
通过对内核代码的分析,我们可以更深入地理解 TCP 协议在 Linux 系统中的实现细节,对于网络应用程序的开发和网络故障的排除都具有重要的意义。