Linux内核源代码解析——TCP状态转移图以及其实现
本文原创为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状态转移图以及其实现的更多相关文章
- Linux内核源代码解析之——我与神童聊Linux内核
本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11619609 我的朋友里,至少有2.5个神童. 有的 ...
- Linux内核源代码解析之——sock's buffer参数
本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11539695 关于socket与sock的关系再简单 ...
- Linux内核源代码解析之TCP面向字节流
本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11264237 大家都知道TCP是面向stream,而 ...
- Linux内核源代码解析——用户发送数据包的起源之sendto
本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/10162853 Jack:我想知道用户如何把数据发送到 ...
- Linux内核源代码情景分析系列
http://blog.sina.com.cn/s/blog_6b94d5680101vfqv.html Linux内核源代码情景分析---第五章 文件系统 5.1 概述 构成一个操作系统最重要的就 ...
- Linux内核源代码获取教程
Linux内核源代码获取方法 什么叫Linux 什么叫Linux内核 Linux内核源代码的获取 什么叫Linux? Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UN ...
- Linux内核中影响tcp三次握手的一些协议配置
在Linux的发行版本中,都存在一个/proc/目录,有的也称它为Proc文件系统.在 /proc 虚拟文件系统中存在一些可调节的内核参数.这个文件系统中的每个文件都表示一个或多个参数,它们可以通过 ...
- 在windows下解压缩Linux内核源代码出现重复文件原因
在windows下解压缩Linux内核源代码出现重复文件原因 2009年06月30日 13:35 来源:ChinaUnix博客 作者:embededgood 编辑:周荣茂 原因一.因为在Lin ...
- Linux内核源代码
说明:只供学习交流 一,目录结构 Linux内核源代码采用树形结构进行组织,非常合理地把功能相关的文件都放在同一个子目录下,使得程序更具有可读性. 二,目录结构 arch目录 arch是archite ...
随机推荐
- Linux操作系统Centos7.2版本搭建Apache+PHP+Mysql环境
对于在校大学生来说腾讯云1元主机很划算,所以就申请了一台,打算在上面学习下linux,使用版本为centos7.2版本.在服务器上比较推荐centos,此版本生命周期较长,而且网上有关centos的教 ...
- java—— 编译与运行
内容:使用javac 指定编译多个目录下java文件 链接:http://zhidao.baidu.com/link?url=W5ZERu8_ouGD-L_JH0vqqawhJNitsGbonQAAT ...
- XML (DOM解析) 看看就行
000000000000000000000000000000000000000 ------------------------------------------------------------ ...
- 2016 cocoapods的安装和使用以及版本升级遇到的问题
一.CocoaPods是什么? CocoaPods是一个负责管理iOS项目中第三方开源库的工具.CocoaPods的项目源码在Github上管理.该项目开始于2011年8月12日,在这两年多的时间里, ...
- hdu 4540 威威猫系列故事——打地鼠 dp小水题
威威猫系列故事——打地鼠 Time Limit: 300/100 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) Total ...
- 使用Fastjson提示No serializer found for class
在调用Json串生成方法时,提示: No serializer found for class com.jeremxy.domain.EpgDetail and no propertiesdiscov ...
- Android 监控网络状态
public static boolean isNetworkAvailable(Context context) { ConnectivityManager connectivity = (Conn ...
- shell 中条件判断
if 中的 -z 到 -d 的意思 2011-09-05 10:30 [ -a FILE ] 如果 FILE 存在则为真. [ -b FILE ] 如果 FILE 存在且是一个块特殊文件则为真. [ ...
- HashSet与HashMap
HashSet底层由HashMap实现 Hash表:存放链表表头的数组 HashSet的值存放于HashMap的key上,而HashMap的value统一为PRESENT(private static ...
- Java中字符串内存位置浅析
前言 之前写过一篇关于JVM内存区域划分的文章,但是昨天接到蚂蚁金服的面试,问到JVM相关的内容,解释一下JVM的内存区域划分,这部分答得还不错,但是后来又问了Java里面String存放的位置,之前 ...