linux内核源码中tcp状态定义,Linux内核源代码解析之——TCP状态转移图以及其实现...

TCP状态转移的原理并不高深,但是处理逻辑比较复杂,以下是TCP状态转移图。出自《TCP/IP协议详解:卷2》——W.Richard Stevens

0818b9ca8b590ca3270a3433284dd417.png

这些状态是怎么实现的呢?

我们来看一下内核源代码。(server端部分)

int

tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,

unsigned long daddr, unsigned short len,

unsigned long saddr, int redo, struct inet_protocol * protocol)

{

struct tcphdr *th;

struct sock *sk;

if (!skb) {

DPRINTF((DBG_TCP, "tcp.c: tcp_rcv skb = NULL\n"));

return(0);

}

#if 0/* FIXME: it's ok for protocol to be NULL */

if (!protocol) {

DPRINTF((DBG_TCP, "tcp.c: tcp_rcv protocol = NULL\n"));

return(0);

}

if (!opt) {/* FIXME: it's ok for opt to be NULL */

DPRINTF((DBG_TCP, "tcp.c: tcp_rcv opt = NULL\n"));

}

#endif

if (!dev) {

DPRINTF((DBG_TCP, "tcp.c: tcp_rcv dev = NULL\n"));

return(0);

}

th = skb->h.th;

/* Find the socket. */

sk = get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);

DPRINTF((DBG_TCP, "<

DPRINTF((DBG_TCP, "len = %d, redo = %d, skb=%X\n", len, redo, skb));

/* If this socket has got a reset its to all intents and purposes

really dead */

if (sk!=NULL && sk->zapped)

sk=NULL;

if (sk) {

DPRINTF((DBG_TCP, "sk = %X:\n", sk));

}

if (!redo) {

if (tcp_check(th, len, saddr, daddr )) {

skb->sk = NULL;

DPRINTF((DBG_TCP, "packet dropped with bad checksum.\n"));

if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");

kfree_skb(skb,FREE_READ);

/*

* We don't release the socket because it was

* never marked in use.

*/

return(0);

}

th->seq = ntohl(th->seq);

/* See if we know about the socket. */

if (sk == NULL) {

if (!th->rst)

tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255);

skb->sk = NULL;

kfree_skb(skb, FREE_READ);

return(0);

}

skb->len = len;

skb->sk = sk;

skb->acked = 0;

skb->used = 0;

skb->free = 0;

skb->saddr = daddr;

skb->daddr = saddr;

/* We may need to add it to the backlog here. */

cli();

if (sk->inuse) {

if (sk->back_log == NULL) {

sk->back_log = skb;

skb->next = skb;

skb->prev = skb;

} else {

skb->next = sk->back_log;

skb->prev = sk->back_log->prev;

skb->prev->next = skb;

skb->next->prev = skb;

}

sti();

return(0);

}

sk->inuse = 1;

sti();

} else {

if (!sk) {

DPRINTF((DBG_TCP, "tcp.c: tcp_rcv bug sk=NULL redo = 1\n"));

return(0);

}

}

if (!sk->prot) {

DPRINTF((DBG_TCP, "tcp.c: tcp_rcv sk->prot = NULL \n"));

return(0);

}

/* Charge the memory to the socket. */

if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {

skb->sk = NULL;

DPRINTF((DBG_TCP, "dropping packet due to lack of buffer space.\n"));

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

sk->rmem_alloc += skb->mem_len;

DPRINTF((DBG_TCP, "About to do switch.\n"));

/* Now deal with it. */

switch(sk->state) {

/*

* This should close the system down if it's waiting

* for an ack that is never going to be sent.

*/

case TCP_LAST_ACK:

if (th->rst) {

sk->zapped=1;

sk->err = ECONNRESET;

sk->state = TCP_CLOSE;

sk->shutdown = SHUTDOWN_MASK;

if (!sk->dead) {

sk->state_change(sk);

}

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

case TCP_ESTABLISHED:

case TCP_CLOSE_WAIT:

case TCP_FIN_WAIT1:

case TCP_FIN_WAIT2:

case TCP_TIME_WAIT:

if (!tcp_sequence(sk, th, len, opt, saddr,dev)) {

if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");

#ifdef undef

/* nice idea, but tcp_sequence already does this. Maybe it shouldn't?? */

if(!th->rst)

tcp_send_ack(sk->sent_seq, sk->acked_seq,

sk, th, saddr);

#endif

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

if (th->rst) {

sk->zapped=1;

/* This means the thing should really be closed. */

sk->err = ECONNRESET;

if (sk->state == TCP_CLOSE_WAIT) {

sk->err = EPIPE;

}

/*

* A reset with a fin just means that

* the data was not all read.

*/

sk->state = TCP_CLOSE;

sk->shutdown = SHUTDOWN_MASK;

if (!sk->dead) {

sk->state_change(sk);

}

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

if (

#if 0

if ((opt && (opt->security != 0 ||

opt->compartment != 0)) ||

#endif

th->syn) {

sk->err = ECONNRESET;

sk->state = TCP_CLOSE;

sk->shutdown = SHUTDOWN_MASK;

tcp_reset(daddr, saddr, th, sk->prot, opt,dev, sk->ip_tos,sk->ip_ttl);

if (!sk->dead) {

sk->state_change(sk);

}

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

if (th->ack && !tcp_ack(sk, th, saddr, len)) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

if (tcp_urg(sk, th, saddr, len)) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

if (tcp_data(skb, sk, saddr, len)) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

/* Moved: you must do data then fin bit */

if (th->fin && tcp_fin(sk, th, saddr, dev)) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

release_sock(sk);

return(0);

case TCP_CLOSE:

if (sk->dead || sk->daddr) {

DPRINTF((DBG_TCP, "packet received for closed,dead socket\n"));

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

if (!th->rst) {

if (!th->ack)

th->ack_seq = 0;

tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);

}

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

case TCP_LISTEN:

if (th->rst) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

if (th->ack) {

tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

if (th->syn) {

#if 0

if (opt->security != 0 || opt->compartment != 0) {

tcp_reset(daddr, saddr, th, prot, opt,dev);

release_sock(sk);

return(0);

}

#endif

/*

* Now we just put the whole thing including

* the header and saddr, and protocol pointer

* into the buffer. We can't respond until the

* user tells us to accept the connection.

*/

tcp_conn_request(sk, skb, daddr, saddr, opt, dev);

release_sock(sk);

return(0);

}

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

default:

if (!tcp_sequence(sk, th, len, opt, saddr,dev)) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

case TCP_SYN_SENT:

if (th->rst) {

sk->err = ECONNREFUSED;

sk->state = TCP_CLOSE;

sk->shutdown = SHUTDOWN_MASK;

sk->zapped = 1;

if (!sk->dead) {

sk->state_change(sk);

}

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

#if 0

if (opt->security != 0 || opt->compartment != 0) {

sk->err = ECONNRESET;

sk->state = TCP_CLOSE;

sk->shutdown = SHUTDOWN_MASK;

tcp_reset(daddr, saddr, th, sk->prot, opt, dev);

if (!sk->dead) {

wake_up_interruptible(sk->sleep);

}

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

#endif

if (!th->ack) {

if (th->syn) {

sk->state = TCP_SYN_RECV;

}

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

switch(sk->state) {

case TCP_SYN_SENT:

if (!tcp_ack(sk, th, saddr, len)) {

tcp_reset(daddr, saddr, th,

sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

/*

* If the syn bit is also set, switch to

* tcp_syn_recv, and then to established.

*/

if (!th->syn) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

/* Ack the syn and fall through. */

sk->acked_seq = th->seq+1;

sk->fin_seq = th->seq;

tcp_send_ack(sk->sent_seq, th->seq+1,

sk, th, sk->daddr);

case TCP_SYN_RECV:

if (!tcp_ack(sk, th, saddr, len)) {

tcp_reset(daddr, saddr, th,

sk->prot, opt, dev,sk->ip_tos,sk->ip_ttl);

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

sk->state = TCP_ESTABLISHED;

/*

* Now we need to finish filling out

* some of the tcp header.

*/

/* We need to check for mtu info. */

tcp_options(sk, th);

sk->dummy_th.dest = th->source;

sk->copied_seq = sk->acked_seq-1;

if (!sk->dead) {

sk->state_change(sk);

}

/*

* We've already processed his first

* ack. In just about all cases that

* will have set max_window. This is

* to protect us against the possibility

* that the initial window he sent was 0.

* This must occur after tcp_options, which

* sets sk->mtu.

*/

if (sk->max_window == 0) {

sk->max_window = 32;

sk->mss = min(sk->max_window, sk->mtu);

}

/*

* Now process the rest like we were

* already in the established state.

*/

if (th->urg) {

if (tcp_urg(sk, th, saddr, len)) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

}

if (tcp_data(skb, sk, saddr, len))

kfree_skb(skb, FREE_READ);

if (th->fin) tcp_fin(sk, th, saddr, dev);

release_sock(sk);

return(0);

}

if (th->urg) {

if (tcp_urg(sk, th, saddr, len)) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

}

if (tcp_data(skb, sk, saddr, len)) {

kfree_skb(skb, FREE_READ);

release_sock(sk);

return(0);

}

if (!th->fin) {

release_sock(sk);

return(0);

}

tcp_fin(sk, th, saddr, dev);

release_sock(sk);

return(0);

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值