java 推送ack_TCP连接建立系列 — 服务端接收ACK段(一)

本文主要分析:三次握手中最后一个ACK段到达时,服务器端的处理路径。

内核版本:3.6

Author:zhangskd @ csdn blog

函数路径

以下是第三次握手时,服务端接收到ACK后的处理路径。

60d957bc3c4c4c8c11b9dcf341ef0dc3.png

接收入口

1. 状态为ESTABLISHED时,用tcp_rcv_established()接收处理。

2. 状态为LISTEN时,说明这个sock处于监听状态,用于被动打开的接收处理,包括SYN和ACK。

3. 当状态不为ESTABLISHED或TIME_WAIT时,用tcp_rcv_state_process()处理。

inttcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)

{

struct sock *rsk;

#ifdef CONFIG_TCP_MD5SIG

/* We really want to reject the packet as early as possible if :

* We're expecting an MD5'd packet and this is no MD5 tcp option.

* There is an MD5 option and we're not expecting one.

*/

if(tcp_v4_inbound_md5_hash(sk, skb))

gotodiscard;

#endif

/* 当状态为ESTABLISHED时,用tcp_rcv_established()接收处理 */

if(sk->sk_state == TCP_ESTABLISHED) {/* Fast path */

struct dst_entry *dst = sk->sk_rx_dst;

sock_rps_save_rxhash(sk, skb);

if(dst) {

if(inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || dst->ops->check(dst,0) == NULL) {

dst_release(dst);

sk->sk_rx_dst = NULL;

}

}

/* 连接已建立时的处理路径 */

if(tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {

rsk = sk;

gotoreset;

}

return0;

}

/* 检查报文长度、报文校验和 */

if(skb->len

gotocsum_err;

/* 如果这个sock处于监听状态,被动打开时的处理,包括收到SYN或ACK */

if(sk->sk_state == TCP_LISTEN) {

/* 返回值:

* NULL,错误

* nsk == sk,接收到SYN

* nsk != sk,接收到ACK

*/

struct sock *nsk = tcp_v4_hnd_req(sk, skb); /* 接收ACK的处理 */

if(! nsk)

gotodiscard;

if(nsk != sk) {/* 接收到ACK时 */

sock_rps_save_rxhash(nsk, skb);

if(tcp_child_process(sk, nsk, skb)) {/* 处理新的sock */

rsk = nsk;

gotoreset;

}

return0;

}

} else

sock_rps_save_rx(sk, skb);

/* 处理除了ESTABLISHED和TIME_WAIT之外的所有状态 */

if(tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {

rsk = sk;

gotoreset;

}

return0;

reset:

tcp_v4_send_reset(rsk, skb); /* 发送RST包 */

discard:

kfree_skb(skb);

return0;

csum_err:

TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);

gotodiscard;

}

收到SYN段后,服务器端会分配一个连接请求块,并初始化这个连接请求块。

构造和发送SYNACK段。

然后把这个连接请求块链入半连接队列中,启动超时定时器。

之后如果再收到ACK,就能完成三次握手了。

staticstruct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)

{

struct tcphdr *th = tcp_hdr(skb);

conststruct iphdr *iph = ip_hdr(skb);

struct sock *nsk;

struct request_sock **prev;

/* 在半连接队列中查找是否已有符合的连接请求块,如果有,则说明这是三次握手的最后一个ACK。*/

struct request_sock *req = inet_csk_search_req(sk, &prev, th->source, iph->saddr, iph->daddr);

if(req)

returntcp_check_req(sk, skb, req, prev);/* 服务器端处理三次握手的最后一个ACK */

/* 如果在半连接队列中没找到,则在ESTABLISHED状态的哈希表中查找。*/

nsk = inet_lookup_established(sock_net(sk), &tcp_hashinfo, iph->saddr, th->source,

iph->daddr, th->dest, inet_iif(skb));

if(nsk) {/* 如果在ehash表中找到对应的sock,且不处于TIME_WAIT状态 */

if(nsk->sk_state != TCP_TIME_WAIT) {

bh_lock_sock(nsk);

returnnsk;

}

inet_twsk_put(ine

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值