套接字处理 核心函数
[/net/ipv4/tcp_input/int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)]
//服务端处理第一次握手
switch (sk->sk_state) {
case TCP_LISTEN:
if (th->syn) {
acceptable = icsk->icsk_af_ops->conn_request(sk, skb) >= 0;
}
}
//客户端/服务器第二次握手处理
switch (sk->sk_state) {
case TCP_SYN_SENT:
//处理SYN_SENT状态下接收到的TCP段
queued = tcp_rcv_synsent_state_process(sk, skb, th);
}
//服务器端的第三次握手
switch (sk->sk_state) {
case TCP_SYN_RECV:
//正常的第三次握手,设置连接状态为TCP_ESTABLISHED
tcp_set_state(sk, TCP_ESTABLISHED);
客户端发出第一次握手
[/net/socket.c/int __sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen)]
sock = sockfd_lookup_light(fd, &err, &fput_needed);//根据文件描述符找到指定的Socket对象
[/net/ipv4/af_inet.c/int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)]
//调用connect函数,对于流式套接字,sock->ops为 inet_stream_ops --> inet_stream_connect --> tcp_prot --> tcp_v4_connect
err = sk->sk_prot->connect(sk, uaddr, addr_len);
//更新socket状态为连接已建立
sock->state = SS_CONNECTED;
[/net/ipv4/tcp_ipv4/int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)]
//套接字状态被置为 TCP_SYN_SENT
tcp_set_state(sk, TCP_SYN_SENT);
//为 TCP报文计算一个初始id
inet->inet_id = prandom_u32();
//函数用来根据 sk 中的信息,构建一个完成的 syn 报文,并将它发送出去。
err = tcp_connect(sk);
**服务器开启第二次握手 **
[/net/ipv4/tcp_ipv4/static int tcp_v4_send_synack(const struct sock *sk, struct dst_entry *dst,
struct flowi *fl,
struct request_sock *req,
struct tcp_fastopen_cookie *foc,
enum tcp_synack_type synack_type)]
//根据路由、传输控制块、连接请求块中的构建SYN+ACK段
skb = tcp_make_synack(sk, dst, req, foc, synack_type);
//生成IP数据报并发送出去
err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr,
ireq->ir_rmt_addr,
ireq->opt);
客户端发出第三次握手
[/net/ipv4/tcp_ipv4/int tcp_v4_rcv(struct sk_buff *skb)]
//创建新的sock进入TCP_SYN_RECV state
nsk = tcp_check_req(sk, skb, req, false);
//服务器端的第三次握手
switch (sk->sk_state) {
case TCP_SYN_RECV:
//正常的第三次握手,设置连接状态为TCP_ESTABLISHED
tcp_set_state(sk, TCP_ESTABLISHED);
参考文献 :
[/net/ipv4/tcp.c/static void tcp_close(struct sock *sk, int timeout)]
//改变状态
sk->sk_shutdown = SHUTDOWN_MASK;
//发送结束信号
if (tcp_close_state(sk)) {
tcp_send_fin(sk);
}
[/net/ipv4/tcp.c/static int tcp_close_state(struct sock *sk)]
//根据当前状态确定下一状态
int next = (int)new_state[sk->sk_state];
int ns = next & TCP_STATE_MASK;
//设置下一状态
tcp_set_state(sk, ns);
//指导是否发出fin
return next & TCP_ACTION_FIN;
第一次挥手
//查看是否有没有发送的数据
struct sk_buff *skb, *tskb = tcp_write_queue_tail(sk);
if (tskb) {//有尾部剩余数据就对其进行发送,同时假设在发送中也包含了Fin
if (tcp_write_queue_empty(sk)) {
//设置tp->snd_nxt为发送Fin后状态
tp->snd_nxt++;
return;
}
} else {
//分配空间并重发数据
skb = alloc_skb_fclone(MAX_TCP_HEADER, sk->sk_allocation);
//重发数据
...
}
第二次握手
P.S.openEuler较为繁琐,使用实例代码
在如下的tcp_ack函数中处理第二次挥手,接收服务发送的ack。
最后客户端把状态改成TCP_FIN_WAIT2,此时服务端的状态是close_wait。服务端等待自己发送fin包。
if (sk->state == TCP_FIN_WAIT1) {
//本端的数据发送完毕
if (sk->rcv_ack_seq == sk->write_seq)
{
sk->shutdown |= SEND_SHUTDOWN;
tcp_set_state(sk, TCP_FIN_WAIT2);
}
}
第三次挥手
[/net/ipv4/tcp_input.c/void tcp_fin(struct sock *sk)]
switch (sk->sk_state) {
case TCP_FIN_WAIT2:
//返回ack信号
tcp_send_ack(sk);
tcp_time_wait(sk, TCP_TIME_WAIT, 0);
break;
}
第四次挥手
第一次,第二次挥手
[/net/ipv4/tcp_input.c/void tcp_fin(struct sock *sk)]
switch (sk->sk_state) {
case TCP_ESTABLISHED:
tcp_set_state(sk, TCP_CLOSE_WAIT);
inet_csk(sk)->icsk_ack.pingpong = 1;
break;
}
第三次挥手
[/net/ipv4/tcp.c/static void tcp_close(struct sock *sk, int timeout)]
//改变状态
sk->sk_shutdown = SHUTDOWN_MASK;
//发送结束信号
if (tcp_close_state(sk)) {
tcp_send_fin(sk);
}
[/net/ipv4/tcp.c/static int tcp_close_state(struct sock *sk)]
//根据当前状态确定下一状态
int next = (int)new_state[sk->sk_state];
int ns = next & TCP_STATE_MASK;
//设置下一状态
tcp_set_state(sk, ns);
//指导是否发出fin
return next & TCP_ACTION_FIN;
第四次挥手
if (sk->state == TCP_LAST_ACK) {
if (sk->rcv_ack_seq == sk->write_seq )
{
flag |= 1;
tcp_set_state(sk,TCP_CLOSE);
//关闭连接
sk->shutdown = SHUTDOWN_MASK;
}
}
参考文献: