linux kernel 三次握手建立TCP链接的实现

1. 应用层

1.1 server

socket() -> bind() -> listen() -> accept() -> recv() & send()

1.2 client

socket() ->  connect() -> send() & recv()

1.3 三次握手建立连接

linux kernel 三次握手建立TCP链接的实现_第1张图片

linux kernel 三次握手建立TCP链接的实现_第2张图片

(1) 客户端发送一个 SYN 段(SYN 标志位置位),包含初始序号 ISN,在图中,这个序号的值 seq = 2379453243. 在这个过程中,客户端是通过 connect 函数发起连接请求的,此时 connect 函数阻塞,等待服务器发回 ACK 应答。

(2) 服务器端接收到 SYN 段后(通过listen监听),知道有新的连接请求到来,于是初始化一个序号 ISN,在上面的例子中这个值是 seq = 4269857. 此时服务器创建一个 TCP 段,将 SYN 和 ACK 标志置位,让 seq = 4269857, ack = 2379453244,然后将这个 TCP 段发送给客户端。这个步骤完全有内核完成,并将此连接加入未完成连接队列。

(3) 客户端再次收到服务器发送来的 TCP 段后,检查到带有 SYN 和 ACK 标志,于是客户端一方连接已经建立成功,此时 connect 函数返回。客户端创建一个 TCP 段,将 ACK 标志置位,同时将 ack 的值设置为 4269858,发送给服务器。

(4) 服务器收到客户端的 ACK 后,将此连接移到已完成连接队列(accept)。

2. kernel流程

2.1 client 通过connect()发送SYN报文,向服务器发起tcp连接

connect ----> SYSCALL_DEFINE3(__sys_connect)(socket.c) -> sock->ops->connect -> inet_stream_connect(af_inet.c) -> __inet_stream_connect -> sk->sk_prot->connect -> tcp_v4_connect(tcp_ipv4.c) -> tcp_connect(tcp_output.c) -> tcp_send_syn_data | tcp_transmit_skb(syn)

tcp_connect(): Build a SYN and send it off

对于阻塞调用,等待后续握手的完成;对于非阻塞调用,则直接返回 -EINPROGRESS,然后在select函数中来捕获socket的连接、读写、异常事件以触发相关操作。

2.2 tcp_rcv_state_process(三次握手状态机)

网卡驱动 --> netif_receive_skb - --> ip_rcv ---> ip_local_deliver_finish ---> tcp_v4_rcv -> tcp_v4_do_rcv -> tcp_rcv_state_process

1) client

客户端收到SYN+ACK报文,然后回ACK报文

case TCP_SYN_SENT: tcp_rcv_synsent_state_process -> tcp_finish_connect & tcp_send_ack

client tcp state: CLOSING ->  TCP_SYN_SENT -> TCP_ESTABLISHED

2) server

a. 收到客户端SYN报文, 发送SYN+ACK报文

case TCP_LISTEN: icsk->icsk_af_ops->conn_request -> tcp_v4_conn_request -> tcp_conn_request -> af_ops->send_synack -> tcp_v4_send_synack

tcp_v4_send_synack(): Send a SYN-ACK after having received a SYN.

server tcp state变为TCP_NEW_SYN_RECV

b. 收到客户端ACK报文

连接已经建立,唤醒阻塞的accept函数。

首先在tcp_v4_rcv中,建一个新的sock进入TCP_SYN_RECV状态;

然后调用tcp_child_process -> tcp_rcv_state_process,最终进入TCP_ESTABLISHED状态,并放入accept队列通知select/epoll。

server tcp state:  CLOSING ->  TCP_LISTEN ->  TCP_NEW_SYN_RECV -> TCP_SYN_RECV ->  TCP_ESTABLISHED

2.3  server通过listen操作开始监听,此时就可以接受到client连接请求。

listen ----> SYSCALL_DEFINE2(listen) -> __sys_listen -> sock->ops->listen --->  inet_csk_listen_start

2.4 accept

accept()实际要做的事件并不多,它的作用是返回一个已经建立连接的socket(即经过了三次握手),这个过程是异步的,accept()并不亲自去处理三次握手过程,而只是监听icsk_accept_queue队列,当有socket经过了三次握手,它就会被加到icsk_accept_queue中,所以accept要做的就是等待队列中插入socket,然后被唤醒并返回这个socket。而三次握手的过程完全是协议栈本身去完成的。换句话说,协议栈相当于写者,将socket写入队列,accept()相当于读者,将socket从队列读出。这个过程从listen就已开始,所以即使不调用accept(),客户仍可以和服务器建立连接,但由于没有处理,队列很快会被占满。

2.5 bind

bind操作的主要作用是将创建的socket与给定的地址相绑定,这样创建的服务才能公开的让外部调用。当然对于socket服务器的创建来说,这一步不是必须的,在listen()时如果没有绑定地址,系统会选择一个随机可用地址作为服务器地址。

你可能感兴趣的:(TCP/IP,Linux,Kernel,linux,三次握手,TCP连接)