本文原创为freas_1990,转载请标明出处http://blog.csdn.net/freas_1990/article/details/10223581

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

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

我们来看一下内核源代码。(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, "<<\n"));
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);
}
}

Linux内核源代码解析——TCP状态转移图以及其实现的更多相关文章

  1. Linux内核源代码解析之——我与神童聊Linux内核

    本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11619609 我的朋友里,至少有2.5个神童. 有的 ...

  2. Linux内核源代码解析之——sock's buffer参数

    本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11539695 关于socket与sock的关系再简单 ...

  3. Linux内核源代码解析之TCP面向字节流

    本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11264237 大家都知道TCP是面向stream,而 ...

  4. Linux内核源代码解析——用户发送数据包的起源之sendto

    本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/10162853 Jack:我想知道用户如何把数据发送到 ...

  5. Linux内核源代码情景分析系列

    http://blog.sina.com.cn/s/blog_6b94d5680101vfqv.html Linux内核源代码情景分析---第五章 文件系统  5.1 概述 构成一个操作系统最重要的就 ...

  6. Linux内核源代码获取教程

    Linux内核源代码获取方法 什么叫Linux 什么叫Linux内核 Linux内核源代码的获取 什么叫Linux? Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UN ...

  7. Linux内核中影响tcp三次握手的一些协议配置

    在Linux的发行版本中,都存在一个/proc/目录,有的也称它为Proc文件系统.在 /proc 虚拟文件系统中存在一些可调节的内核参数.这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 ...

  8. 在windows下解压缩Linux内核源代码出现重复文件原因

    在windows下解压缩Linux内核源代码出现重复文件原因 2009年06月30日 13:35 来源:ChinaUnix博客 作者:embededgood 编辑:周荣茂     原因一.因为在Lin ...

  9. Linux内核源代码

    说明:只供学习交流 一,目录结构 Linux内核源代码采用树形结构进行组织,非常合理地把功能相关的文件都放在同一个子目录下,使得程序更具有可读性. 二,目录结构 arch目录 arch是archite ...

随机推荐

  1. server的散热和Linux中温度的检測

    当server被放在散热条件不好的条件下,这样会导致硬盘驱动过早损坏,而且server其它的组件也会非常快出现问题. 现代的server主板检測到CPU过热的时候,一般会限制CPU的频率,所以即使se ...

  2. ASSERT_VALID和ASSERT宏分析

    这个宏都是MFC的调试宏. ASSERT_VALID宏用来在运行时检查一个对象的内部合法性,比如说现在有一个学生对象,我们知道每个学生的年龄一定大于零,若年龄小于零,则该学生对象肯定有问题. 事实上, ...

  3. linux下常用基本命令操作

    #fdisk -l 查看硬盘信息 cat /proc/cpuinfo 查看CPU信息 free -m 查看内存信息 ethtool eth0 查看网卡信息 df -h 查看硬盘各分区可用空间大小 ca ...

  4. Python基础----函数

    1.作用域: 对于变量的作用域,执行声明并在内存中存在,该变量就可以在下面的代码中使用. if 1==1: name = 'wupeiqi' print name 下面的结论对吗? 外层变量,可以被内 ...

  5. ORA-00214: controlfile '/u01/app/oracle/oradata/[sid]/control01.ctl' version inconsistent with file '/u01/app/oracle/oradata/[sid]/control03.ctl'

    Sample error: SQL> startupORACLE instance started. Total System Global Area 285212672 bytesFixed ...

  6. 以下是jQuery和JavaScript实现相同操作的等价代码。

    选择元素  Javascript代码 1.// jQuery   2.var els = $('.el');   3.  4.// 原生方法   5.var els = document.queryS ...

  7. IO流(数据流

    数据流 创建一个输入或者输出流只想底层的输入输出流 File file1=new File("test1.txt"); FileOutputStream out1=new File ...

  8. querySelector选择器

    querySelector选择器可以通过document和element来调用他们 用来代替getElementById var body=document.querySelector("b ...

  9. win7下搭建nginx+php的开发环境

    本来在win7下用的是IIS做web服务器,但近来因项目需求的原因,需要在服务器遇到404错误的时候自动做转向(不是在客户端的跳转,而是在服务器收到客户端请求去某目录下读取文件返回时,如果发现目录或目 ...

  10. AIDL Service

    开发AIDL服务的步骤 AIDL(Android Interface Definition Language)是Service的一种重要应用,允许一个应用程序访问另一个应用程序中的对象. 建立AIDL ...