linux社区爱心援助Linux认证系列教程业界动态站务新闻公司招聘建议留言网址大全LPI专题CISCO专题
设为首页
加入收藏
管理团队
JSP  
JAVA  
PERL  
 您的位置:首页 > article > Linux开发区 > 内核研究 >
栏目导栏
资料搜索
热门文章
·linux-2.6内核升级文档
·Linux源代码的注释
·linux2.6内核编译方法详述
·Linux操作系统内核编译详解
·Linux内核结构详解
·Linux配置与编译内核
·定时器
·Linux内核升级全攻略
·FC5(Fedora Core5)下编译内核总
·Ubuntu 6.06 dapper 内核编译初
·Linux 内核编译详解
·Linux内核管理基础知识概述
·Linux2.4升级到2.6内核升级指南
·第八章 设备驱动
·如何编译linux内核
最新文章
·Linux内核bootsplash功能的实现
·Linux内核2.6.25全新发布加入众
·Debian Linux系统编译内核标准
·Linux2.4内核和2.6内核对Initr
·2.6.24内核编译 initrd-2.6.24
·Qtopia应用程序与Linux内核数据
·Linux 2.6内核中sysfs文件系统
·Linux2.6内核驱动移植参考
·Andrew Morton:Linux内核的执法
·Fedora 8 Linux系统的内核配置
·Kernel中的irq.c函数
·Linux核心出现权限扩张及记忆体
·Linux 2.6本地权限提升漏洞
·结合Linux系统内核源码理解SYN
·关于Linux 内核中五个主要子系
Google
 
结合Linux系统内核源码理解SYN_RECV状态
[ 作者:  加入时间:2008-01-08 11:08:28  来自:Linux联盟收集整理 ]
 

在tcp_v4_do_rcv中,有下面一段代码,是关于TCP连接建立时候的代码:

if (sk->sk_state == TCP_LISTEN) {
struct sock *nsk = tcp_v4_hnd_req(sk, skb);
if (!nsk)
goto discard;

if (nsk != sk) {
if (tcp_child_process(sk, nsk, skb))
goto reset;
return 0;
}
}

tcp_v4_hnd_req的返回值,不同情况下不同。

NULL 出现错误

nsk==sk 接受到SYN

nsk!=sk 接受到ACK

接受到ACK包时,tcp_v4_hnd_req函数会新建一个sock结构,并设置其初始状态为SYN_RECV,并返回新建的sock结构。

接着调用tcp_child_process函数,改变新建的sock的状态为ESTABLISHED。

(以下基于linux内核2.4.0)

SYN_RECV状态,顾名思义,是收到SYN包后应该置的状态。关于SYN_RECV状态,受某些教科书的误导,我以前一直理解为服务器收到SYN包后应该置此状态。也没细想到底是置那个socket的状态,最近在看三次握手协议在linux内核中的实现时,才仔细思考这个问题应该是置连接套接字的状态而非监听套接字的状态。

通常,SYN包只用于TCP三次握手协议中。常见的tcp三次握手协议过程(当然还有同时连接、

半连接等其它一些情况)如下:

1、client SYN包---> server

2、client <---SYN包/ACK包 server

3、client ACK包---> server

根据tcp状态图,对应下述4个状态的变化

a、client发送完毕,状态变成SYN_SEND;

b、server收到SYN报并发送ack确认包和SYN包,状态变为SYN_RECV

c、client发送ack包完毕,状态变成ESTABLISHED

d、server发送ack包完毕,状态变成ESTABLISHED

在linux内核中,上述几个状态对应为TCP_SYN_SEND、TCP_SYN_RECV、TCP_ESTABLISHED.

RFC793中关于SYN_RECV状态的描述如下:

SYN-RECEIVED - represents waiting for a confirming connection

request acknowledgment after having both received and sent a

connection request.

从上面可以看出,这个状态是在本端接收到对端连接请求,并发送连接对端请求后,等待对端应答时所置的状态。所以,本质上连接的过程是双方请求应答的来回, 应该称四次握手,只是常见的应用以c/s模式为主,而linux、包括绝大部分操作系统都把服务器端的应答和请求封装在一个包里面。

但在linux内核中,却是在监听套接字收到了客户端的ACK包后,才创建连接套接字并初始化为TCP_SYN_RECV状态,如下函数调用关系:

tcp_v4_rcv-->tcp_v4_do_rcv-->tcp_v4_hnd_req-->tcp_check_req-->

tcp_v4_syn_recv_sock-->tcp_create_openreq_child...

struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb)

{

struct sock *newsk = sk_alloc(PF_INET, GFP_ATOMIC, 0); /*创建连接sock结构*/

if(newsk != NULL) {

struct tcp_opt *newtp;

...

memcpy(newsk, sk, sizeof(*newsk));

newsk->state = TCP_SYN_RECV; /*置初始状态为SYN_RECV*/

//以下为一些初始化newsk结构的操作

...

}

这里似乎都正常了,但还有一点,服务器收到ACK包后,状态应该改为连接状态,而此时连接套接字的状态还是TCP_SYN_RECV

原因在于现在对ack包还没处理完,^_^,如下:

int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)

{

...

if (sk->state == TCP_LISTEN) { //此处是监听套接字的状态

struct sock *nsk = tcp_v4_hnd_req(sk, skb); //获得了上面讲的连接套接字

if (!nsk)

goto discard;

if (nsk != sk) { //显然监听与连接套接字不等

if (tcp_child_process(sk, nsk, skb)) //此处调用tcp_rcv_state_process置套接字为连接建立状态

goto reset;

return 0;

}

}

...

}

可见,在linux内核中,SYN_RECV状态的保持时间是非常短暂的(也很难创建条件让此状态保持),这也是我们实际应用中通过netstat基本看不到这个状态的原因。

Linux联盟收集整理 ,转贴请标明原始链接,如有任何疑问欢迎来本站Linux论坛讨论
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
 ·Linux系统下的Shell循环语句  (2008-01-08 11:07:47)
 ·Linux操作系统下安装配置jython编程环境  (2008-01-08 11:06:53)
 ·Linux操作系统下如何配置Qt(4.3.2)环境  (2008-01-08 11:06:24)
 ·在Linux操作系统下单网卡实现双IP的设置  (2008-01-08 11:05:41)
 ·解决Linux系统中关于Weblogic的设置问题  (2008-01-08 11:04:58)
 ·关于Linux操作系统的网卡编译  (2008-01-08 11:04:16)
 ·Ubuntu Linux系统下的两个网络扫描工具  (2008-01-07 10:13:30)
 ·Ubuntu 7.10扩展Linux系统的多媒体应用  (2008-01-07 10:12:35)
 ·最简单的Linux系统上的pptpd服务器安装  (2008-01-07 10:09:37)
 ·Linux系统的股票行情分析软件  (2008-01-07 10:07:00)