设置网站默认首页,中国最大的家装网站,东莞市塘厦镇,东营网红餐厅文章目录 1. 前言2. 什么是 TCP 序列号#xff1f;3. TCP 序号 的 初始值设置 和 后续变化过程3.1 三次握手 连接建立 期间 客户端 和 服务端 序号 的 变化过程3.1.1 客户端 socket 初始序号 的 建立3.1.2 服务端 socket 初始序号 的 建立3.1.3 客户端 socket 接收 服务端 SAC… 文章目录 1. 前言2. 什么是 TCP 序列号3. TCP 序号 的 初始值设置 和 后续变化过程3.1 三次握手 连接建立 期间 客户端 和 服务端 序号 的 变化过程3.1.1 客户端 socket 初始序号 的 建立3.1.2 服务端 socket 初始序号 的 建立3.1.3 客户端 socket 接收 服务端 SACK然后回复 服务端 ACK3.1.4 服务端 接收 客户端 ACK完成 三次握手3.1.5 三次握手期间的序号变化总结 3.2 连接建立后数据 通信 过程中 序号 的变化3.3 四次挥手 连接断开 期间 序号 的变化 1. 前言
限于作者能力水平本文可能存在谬误因此而给读者带来的损失作者不做任何承诺。
2. 什么是 TCP 序列号
TCP 序列号是为 TCP 协议通信数据中的每一个字节赋予一个唯一编号其作用可以概括如下
1. 数据分段(Segment)与重组一次发送的数据长度如果超过设定的 MSS(Maximum Segment Size)就会被分成多个带有 TCP 协议头的段(Segment)分别发送出去接收端需将接收的分段(Segment)数据按每个 TCP 数据段(Segment)头部中的序列号来进行重组。
2. 数据的可靠性传输接收端在收到发送端的数据后通过 ACK 返回接收数据的序列号这样发送端就可以确认数据已抵达接收端。
3. 流量控制TCP 通过滑动窗口来进行流量控制。通信两端各自维护自身的 发送 和 接收滑动窗口大小并将自身接收滑动窗口大小 和 序列号 发送给对端让对端知道当前最大可以发送的数据量。
4. 防止重复报文TCP 有超时重发机制如果发送的数据超过一定时间没有收到对方的确认ACK则会认为数据可能丢失而进行重发。在有些情形下数据并没有丢失只是因为某些原因导致在发送路途中消耗时间过长如果这个时间超过了 TCP 的超时重发时间则接收端会重复接收到同一数据这时可通过丢弃具有相同序列号的数据来去重。一图胜千言还是上一张图来直观感受下 上图是 TCP 协议数据头部红框中的部分序号 表示发送端所发送数据的序列号确认号 表示接收端已收到发送端序号为确认号之前的所有数据回送确认号告诉发送端可以继续发送序号从确认号开始的数据。TCP 序列号以一个 32 位无符号数 表示最大值为 2^32 - 1到达最大值后回卷到 0 。
3. TCP 序号 的 初始值设置 和 后续变化过程
从前面的章节中我们对 TCP 序号 有了一个初步了解本文剩余部分将结合图示和内核源码来说说 连接建立初期 TCP 序号初始值的设定过程、以及 连接建立后 TCP 通信当中序号的变化过程。 在正式讨论 TCP 序号 的相关细节前首先要明确的是TCP 序号 是基于每 socket 进行维护的即每个 socket 都有自己独立的序号从后面的代码分析我们将看到这一点。 本文以典型的 TCP 服务端 和 客户端 通信过程说明 TCP 序号 的维护细节。本文基于 Linux 4.14 内核源码进行分析。先上两张图分别描述了 TCP 套接字状态机 和 TCP socket 通信序列号变化过程(包括 三握四挥、数据传输)如下
3.1 三次握手 连接建立 期间 客户端 和 服务端 序号 的 变化过程
TCP 服务端 和 客户端 socket 各自的 序号初始值 在连接建立的 三次握手 过程中建立分析过程参考上面的第二张图进行(我们假定 服务端 当前处于 LISTEN 态(TCP_LISTEN)(即已经调用了 listen())。
3.1.1 客户端 socket 初始序号 的 建立
客户端 socket 通过 connect() 系统调用构建 SYN 包 向 服务端 发起连接请求其 初始序号 建立的主要流程如下
sys_connect()...tcp_v4_connect()...tcp_set_state(sk, TCP_SYN_SENT); /* 标记 客户端 socket 进入 TCP_SYN_SENT 状态 */...tp-write_seq secure_tcp_seq(...); /* 初始化 客户端 socket 序号 为 随机值 x (假定 x 4065008942) */...tcp_connect(sk);struct tcp_sock *tp tcp_sk(sk); /* 客户端 TCP socket 对象 */struct sk_buff *buff;...buff sk_stream_alloc_skb(sk, 0, sk-sk_allocation, true); /* 为 SYN 包分配空间 */.../** 初始化 TCP SYN 包 TCP 控制块(tcp_skb_cb): * . 设置 SYN 包 初始 序号* . 设置 SYN 包 SYN 标志*/tcp_init_nondata_skb(buff, tp-write_seq, TCPHDR_SYN);TCP_SKB_CB(skb)-tcp_flags flags; /* 设置 SYN 标志位 */TCP_SKB_CB(skb)-sacked 0;...TCP_SKB_CB(skb)-seq seq; /* SYN 包 序号 设为 x (x 4065008942) */......tcp_transmit_skb(sk, buff, 1, sk-sk_allocation);__tcp_transmit_skb(sk, skb, clone_it, gfp_mask, tcp_sk(sk)-rcv_nxt);struct tcphdr *th; /* TCP 数据包头部 */...th-seq htonl(tcb-seq); /* 客户端 发往 服务端 SYN 包序号 为 x (x 4065008942) */th-ack_seq htonl(rcv_nxt); /* 客户端 发往 服务端 的 SYN 包确认号 为 0 *//* 设置 SYN 标志位 */*(((__be16 *)th) 6) htons(((tcp_header_size 2) 12) | tcb-tcp_flags);......tp-snd_nxt tp-write_seq; /* 更新 套接字 的 发送滑动窗口 下一要发送字节 的 序号 */...从上面的代码分析看到客户端 socket 在 connect() 中构建一个 SYN 包在 SYN 包 构建过程中确定了 初始序号且设置了 SYN 标志位。此时 客户端 socket 的状态 也由 CLOSED(TCP_CLOSED) 转化为 SYN-SENT(TCP_SYN_SENT) 。用 tcpdump 在 服务端 抓包结果如下
00:30:27.921869 IP 169.228.88.168.52524 169.228.88.188.8888: Flags [S], seq 4065008942, win 64240, options [mss 1460,sackOK,TS val 3875254422 ecr 0,nop,wscale 7], length 0可以看到服务端 169.228.88.188.8888 收到了从 客户端 169.228.88.168.52524 发送的 SYN 包客户端初始序号 为 x4065008942 。tcpdump 用 [S] 标记 SYN 包。
3.1.2 服务端 socket 初始序号 的 建立
服务端 收到 客户端 序号 为 x 的 SYN 包 后会回复 确认号 为 x1 的 ACK 给 客户端同时构建一个 序号 为 y 的 SYN 包向 客户端 发起连接请求。很自然的服务端 将 回复 ACK 和 请求 SYN 放在同一个 TCP 包里一同发往 客户端而不是分开发送这就是平常所见的 SACK 包或者 SYN ACK 包。前述过程的主要代码流程如下
/* * 从网卡接收数据中断接口开始: * 服务端 收到 客户端 的 SYN 连接请求后回复 客户端 SYN ACK.*/
xxx_nic_interrput()...ip_rcv()...tcp_v4_rcv()...const struct iphdr *iph; /* IP 头部 */const struct tcphdr *th; /* TCP 头部 */...struct sock *sk; /* TCP 服务端 socket 对象 */.../* 找到 TCP 服务端 socket 对象 */th (const struct tcphdr *)skb-data;iph ip_hdr(skb);lookup:sk __inet_lookup_skb(tcp_hashinfo, skb, __tcp_hdrlen(th), th-source,th-dest, sdif, refcounted); .../* 从 客户端 socket 发送的 SYN 包 提取 序号 x (x 4065008942)、确认号 等(到 TCP 控制块 struct tcp_skb_cb) */th (const struct tcphdr *)skb-data;iph ip_hdr(skb);tcp_v4_fill_cb(skb, iph, th);TCP_SKB_CB(skb)-seq ntohl(th-seq); /* TCP_SKB_CB(skb)-seq x; (x 4065008942) */...TCP_SKB_CB(skb)-ack_seq ntohl(th-ack_seq); /* 0 */...TCP_SKB_CB(skb)-tcp_flags tcp_flag_byte(th); /* SYN */TCP_SKB_CB(skb)-tcp_tw_isn 0;......if (sk-sk_state TCP_LISTEN) { /* TCP 服务端 socket 当前处于 LISTEN 状态(TCP_LISTEN) */ret tcp_v4_do_rcv(sk, skb);...tcp_rcv_state_process(sk, skb))struct tcp_sock *tp tcp_sk(sk);...const struct tcphdr *th tcp_hdr(skb);...switch (sk-sk_state) {...case TCP_LISTEN: /* TCP 服务端 socket 当前处于 LISTEN 状态(TCP_LISTEN) */...if (th-syn) { /* 是 客户端 socket 发送的 SYN 包(设置了 SYN 标志位) */.../* 调用 tcp_v4_conn_request(), 见后续分析 */acceptable icsk-icsk_af_ops-conn_request(sk, skb) 0;...consume_skb(skb); /* 消耗 客户端 socket 的 SYN 包 */return 0;}......}......}/* 接上面的分析 */
acceptable tcp_v4_conn_request(sk, skb) 0;tcp_conn_request(tcp_request_sock_ops, tcp_request_sock_ipv4_ops, sk, skb);...__u32 isn TCP_SKB_CB(skb)-tcp_tw_isn; // 0.../* 服务端 TCP socket, 在关闭前会一直处于 TCP_LISTEN 态, 通过 accept() 接收客户端的 SYN 连接请求 */struct tcp_sock *tp tcp_sk(sk);.../* 即将 在 服务端 新建一个 socket 对象, 和 客户端 发起连接 的 socket 通信 */struct request_sock *req;.../** !!!* 为 客户端 连接请求 分配 轻量 socket 对象 struct request_sock.* 记住 这个 轻量 socket 对象它将用于 服务端 在接收 客户端 ACK 的处理.*/req inet_reqsk_alloc(rsk_ops, sk, !want_cookie); /* (1) */struct request_sock *req reqsk_alloc(ops, sk_listener, attach_listener);struct request_sock *req;req kmem_cache_alloc(ops-slab, GFP_ATOMIC | __GFP_NOWARN);...if (attach_listener) {.../* * 记录 客户端 连接信息 的 轻量 socket 是 向 服务端 socket 对象 sk_listener* 发起的即 req-rsk_listener 记录了 服务端 socket 对象。*/req-rsk_listener sk_listener;}...if (req) {struct inet_request_sock *ireq inet_rsk(req);.../* 记录 客户端 连接的 轻量 socket 初始为 SYN-RECEIVED(TCP_NEW_SYN_RECV) 态 */ireq-ireq_state TCP_NEW_SYN_RECV;write_pnet(ireq-ireq_net, sock_net(sk_listener));ireq-ireq_family sk_listener-sk_family;}...tcp_rsk(req)-af_specific af_ops; /* tcp_request_sock_ipv4_ops */...tcp_openreq_init(req, tmp_opt, skb, sk);...req-rsk_rcv_wnd 0;.../* 记录 客户端 的 初始序号 x (x 4065008942) */tcp_rsk(req)-rcv_isn TCP_SKB_CB(skb)-seq; /* tcp_rsk(req)-rcv_isn x; *//* 服务端 期待 收到 的 下一 客户端 数据的 序号 x 1 (x 1 4065008942 1 4065008943) */tcp_rsk(req)-rcv_nxt TCP_SKB_CB(skb)-seq 1; /* tcp_rsk(req)-rcv_nxt x 1; */....../* 为 服务端 新建的、用来和 客户端 通信的 socket 生成 初始 序号(ISN: Initial Sequence Number) */if (!want_cookie !isn) {...isn af_ops-init_seq(skb); /* tcp_v4_init_seq() */tcp_v4_init_seq(skb)secure_tcp_seq(...)}/* 设定 服务端 新建的、用来和 客户端 通信 socket 的 初始 序号 y (假定 y 1093122830) */tcp_rsk(req)-snt_isn isn;...if (fastopen_sk) {...} else {if (!want_cookie)/* 将记录 客户端 连接的 轻量级 的 socket 添加到 服务端 socket 的 SYN 队列(半连接队列) */inet_csk_reqsk_queue_hash_add(sk, req, tcp_timeout_init((struct sock *)req));/* 服务端 回复 客户端 ACK同时发送 SYN 连接请求 (tcp_v4_send_synack()) */af_ops-send_synack(sk, dst, fl, req, foc,!want_cookie ? TCP_SYNACK_NORMAL : TCP_SYNACK_COOKIE);tcp_v4_send_synack(sk, dst, fl, req, foc, TCP_SYNACK_NORMAL).../** 构建 SYN ACK 包: * . ACK 是对 客户端 SYN 连接请求的回复;* . SYN 是 服务端 向 客户端 发起的连接请求.*/skb tcp_make_synack(sk, dst, req, foc, synack_type);struct tcphdr *th;...skb alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC); /* 分配 skb 空间 */...th (struct tcphdr *)skb-data;memset(th, 0, sizeof(struct tcphdr));th-syn 1; /* 设置 SYN 标志位 */th-ack 1; /* 设置 ACK 标志位 */.../* th-seq y; (y 1093122830) */th-seq htonl(tcp_rsk(req)-snt_isn);/* th-ack_seq x 1; (x 1 4065008942 1 4065008943) */th-ack_seq htonl(tcp_rsk(req)-rcv_nxt);...if (skb) {.../* 服务端 回复 客户端 SYN 请求 ACK, 同时向 客户端 发送 连接请求 SYN */err ip_build_and_send_pkt(skb, sk, ireq-ir_loc_addr,ireq-ir_rmt_addr,rcu_dereference(ireq-ireq_opt));...}}3.1.3 客户端 socket 接收 服务端 SACK然后回复 服务端 ACK
接下来 客户端 socket 收到 服务端 的 SYN ACK 包其中 ACK 是 服务端 对 客户端 SYN 的回复而 SYN 是来自 服务端 的连接请求。从 3.1.1 的分析了解到客户端套接字当前处于 SYN-SENT(TCP_SYN_SENT) 状态。来看 客户端 收取 服务端 ACK 并 回复 服务端 SYN 的 ACK 的代码细节
/** 从网卡接收数据中断接口开始:* 客户端 接收 服务端 对 自身 SYN 请求的 ACK, 并对 服务端 的 SYN 请求 回复 ACK.*/
xxx_nic_interrput()...ip_rcv()tcp_v4_rcv()...if (!sock_owned_by_user(sk)) {ret tcp_v4_do_rcv(sk, skb);...tcp_rcv_state_process(sk, skb)...switch (sk-sk_state) {...case TCP_SYN_SENT: /* 客户端 socket 当前处于 SYN-SENT(TCP_SYN_SENT) 态 */queued tcp_rcv_synsent_state_process(sk, skb, th); /* 见后续分析 */...return 0;...}} else if (tcp_add_backlog(sk, skb)) {...}.../* 接上面的分析 */
queued tcp_rcv_synsent_state_process(sk, skb, th);...if (th-ack) { /* 客户端 收到 服务端 对 SYN 回复的 SYN ACK */...if (!th-syn) /* 不是 (SYN ACK) */goto discard_and_undo;.../* 客户端 期待的 服务端 的下一数据 序号 y 1 */tp-rcv_nxt TCP_SKB_CB(skb)-seq 1; /* tp-rcv_nxt y 1; (y 1 1093122830 1 1093122831) */tp-rcv_wup TCP_SKB_CB(skb)-seq 1;.../** 客户端 完成 连接建立工作* 客户端 socket 由 SYN-SENT(TCP_SYN_SENT) 转为 ESTABLISHED(TCP_ESTABLISHED) 态. */tcp_finish_connect(sk, skb);...tcp_set_state(sk, TCP_ESTABLISHED); /* 客户端 socket 进入 ESTABLISHED(TCP_ESTABLISHED) 态 */......if (...) {...} else {tcp_send_ack(sk); /* 构建 向 服务端 回送 ACK 包 */__tcp_send_ack(sk, tcp_sk(sk)-rcv_nxt);struct sk_buff *buff;...buff alloc_skb(MAX_TCP_HEADER, ...);...tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK);...__tcp_transmit_skb(sk, buff, 0, (__force gfp_t)0, rcv_nxt);...struct tcp_skb_cb *tcb;...struct tcphdr *th;...tcb TCP_SKB_CB(skb);...th (struct tcphdr *)skb-data;.../* th-seq x 1; (x 1 4065008942 1 4065008943) */th-seq htonl(tcb-seq);/* th-ack_seq y 1; (y 1 1093122830 1 1093122831) */th-ack_seq htonl(rcv_nxt); /* 设置 TCP 头部的 ACK 标记 */*(((__be16 *)th) 6) htons(((tcp_header_size 2) 12) | tcb-tcp_flags);.../* 将 ACK 向下传递给 网络层 发送出去 */err icsk-icsk_af_ops-queue_xmit(sk, skb, inet-cork.fl); /* ip_queue_xmit() */}...}至此客户端 服务端 的 单边连接 已经建立客户端 socket 已经进入 ESTABLISHED(TCP_ESTABLISHED) 状态。用 tcpdump 在 服务端 抓包结果如下
00:30:28.005237 IP 169.228.88.188.8888 169.228.88.168.52524: Flags [S.], seq 1093122830, ack 4065008943, win 65160, options [mss 1460,sackOK,TS val 3225418910 ecr 3875254422,nop,wscale 5], length 0可以看到服务端 169.228.88.188.8888 向 客户端 169.228.88.168.52524 回复了 一个 SACK 包(SYN ACK)服务端初始序号 为 y1093122830确认号 为 x14065008943 。tcpdump 用 [S.] 标记 SACK 包。
3.1.4 服务端 接收 客户端 ACK完成 三次握手
从上一小节 3.1.3 了解到客户端 服务端 的 单边连接 已经建立但 服务端 客户端 的 单边连接 尚未得到确认(即 服务端 还没有收取 客户端 对 SYN 的 ACK 回复)。下面来看 服务端 接收 客户端 ACK 完成 三次握手 的代码细节
xxx_nic_interrput()...ip_rcv()tcp_v4_rcv()...const struct iphdr *iph;const struct tcphdr *th;......th (const struct tcphdr *)skb-data;iph ip_hdr(skb);lookup:/* * !!! * 注意这里查找到的 socket 对象不再是 服务端 socket 套接字* 而是 服务端 收到 客户端 SYN 时 新建的、记录了 客户端连接信息* 的 轻量 socket 对象 struct request_sock 即 3.1.2 代码分* 析注释中、 标记 (1) 处建立的 socket 对象。*/sk __inet_lookup_skb(tcp_hashinfo, skb, __tcp_hdrlen(th), th-source,th-dest, sdif, refcounted);...if (sk-sk_state TCP_NEW_SYN_RECV) {struct request_sock *req inet_reqsk(sk); /* 服务端 记录 客户端连接信息的 轻量 socket */sk req-rsk_listener; /* sk 服务端 监听 socket */...if (!tcp_filter(sk, skb)) {th (const struct tcphdr *)skb-data;iph ip_hdr(skb);tcp_v4_fill_cb(skb, iph, th);/* TCP_SKB_CB(skb)-seq x 1; (x 1 4065008942 1 4065008943) */TCP_SKB_CB(skb)-seq ntohl(th-seq);/* TCP_SKB_CB(skb)-ack_seq y 1; (y 1 1093122830 1 1093122831) */TCP_SKB_CB(skb)-ack_seq ntohl(th-ack_seq);...TCP_SKB_CB(skb)-tcp_flags tcp_flag_byte(th); /* ACK */TCP_SKB_CB(skb)-tcp_tw_isn 0;......}...}至此客户端 服务端 的双向连接 已经建立完成。用 tcpdump 在 服务端 抓包结果如下
00:30:28.006550 IP 169.228.88.168.52524 169.228.88.188.8888: Flags [.], ack 1093122831, win 502, options [nop,nop,TS val 3875254506 ecr 3225418910], length 0可以看到服务端 169.228.88.188.8888 收到 客户端 169.228.88.168.52524 发送的 ACK确认号 为 y1 1093122831 。tcpdump 用 [.] 标识 ACK 。
3.1.5 三次握手期间的序号变化总结
下面描述中的 seq 表示 序号ack_req 表示 确认号flags 表示 TCP 协议头部标记(SYN,ACK 等)。
1. 客户端 向 服务端 发起 SYN 连接请求SYN 包的 TCP 头部中: seqx, ack_req0, flagsSYN;
2. 服务端 收到 客户端 的 SYN 后回复 客户端 SACK 包(SYNACK)SACK 包的 TCP 头部中: seqy, ack_reqx1, flagsSYN|ACK;
3. 客户端 收到 的 SACK 后回复 服务端 一个 ACK 包ACK 包的 TCP 头部中seqx1, ack_reqy1 。可以看到通信两端(服务端 和 客户端)建立 初始序号 后在 三次握手 期间各自的 SYN 消耗了 1 个序号最终通信两端 的 序号 分别 停在了 x1 和 y1 上。
3.2 连接建立后数据 通信 过程中 序号 的变化
在连接建立后通信两端分别在当前序号 x1 和 y1 的基础上每次以发送数据长度递增序号发往对端数据的 确认号 为 接收到的对端数据中 序号 加 1。看一下 tcpdump 抓包情况
00:30:57.585899 IP 169.228.88.168.52524 169.228.88.188.8888: Flags [P.], seq 4065008943:4065009967, ack 1093122831, win 502, options [nop,nop,TS val 3875284086 ecr 3225418910], length 1024
00:30:57.605209 IP 169.228.88.188.8888 169.228.88.168.52524: Flags [.], ack 4065009967, win 2005, options [nop,nop,TS val 3225448498 ecr 3875284086], length 0可以看到客户端 169.228.88.168.52524 向 服务端 169.228.88.188.8888 发送了 1024 个字节数据 序号 位于 半开半闭 区间 [x14065008943, x10244065009967) (不包括 4065009967)而 服务端 169.228.88.188.8888 确认收到 客户端 169.228.88.168.52524 的所有 1024 个字节后回复 客户端 169.228.88.168.52524 一个 ACK其 确认号为 x10244065009967 。
3.3 四次挥手 连接断开 期间 序号 的变化
本文不对 四次挥手 期间 序号 的变化过程进行分析感兴趣的读者可参考博文 LinuxTCP三握四挥简析自行阅读源码分析。看一下 tcpdump 抓包情况
00:30:57.605252 IP 169.228.88.168.52524 169.228.88.188.8888: Flags [F.], seq 4065009967, ack 1093122831, win 502, options [nop,nop,TS val 3875284086 ecr 3225418910], length 0
00:30:57.669707 IP 169.228.88.188.8888 169.228.88.168.52524: Flags [.], ack 4065009968, win 2005, options [nop,nop,TS val 3225448552 ecr 3875284086], length 0
00:31:02.468946 IP 169.228.88.188.8888 169.228.88.168.52524: Flags [F.], seq 1093122831, ack 4065009968, win 2005, options [nop,nop,TS val 3225453368 ecr 3875284086], length 0
00:31:02.469779 IP 169.228.88.168.52524 169.228.88.188.8888: Flags [.], ack 1093122832, win 502, options [nop,nop,TS val 3875288970 ecr 3225453368], length 0可以看到
1. 客户端 169.228.88.168.52524 主动发起关闭(调用 close())向 服务端 169.228.88.188.8888 发送 序号 为 x10244065009967、确认号 为 y11093122831 的 FIN 包。客户端 发送了 1024 个字节后再没有数据发送所以 FIN 包 序号 紧接数据序号 之后同时由于 服务端 没有 发送数据所以 确认号 没有变化。
2. 服务端 169.228.88.188.8888 回复 客户端 169.228.88.168.52524 的 FIN 包 一个 ACK 确认号 为 x102414065009968。至此客户端 到 服务端 的单向连接 已经断开。
3. 服务端 169.228.88.188.8888 向 客户端 169.228.88.168.52524 发送 FIN 包请求断开连接该包的 序号 为y11093122831确认号 为 x102414065009968因为 客户端 到 服务端 的单向连接 已经断开不会再有从 客户端到 服务端 的数据所以 确认号 不再变换。
4. 客户端 169.228.88.168.52524 回复 服务端 169.228.88.188.8888 的 FIN 包 一个 ACK 确认号 为 y111093122832。至此服务端 到 客户端 的单向连接 已经断开于是整个连接彻底断开。