TCP状态图和常见问题

来源:互联网 发布:剑雨逍遥进阶数据图片 编辑:程序博客网 时间:2024/06/10 04:33

TCP共有9个网路状态,其中涉及到关闭的状态有5个。

       在我们编写网络相关程序的时候,这5个状态经常出现。因为这5个状态相互关联,相互纠缠,而且状态变化触发都是由应用触发,但是又涉及操作系统和网络,所以正确的理解TCP 在关闭时网络状态变化情况,为我们诊断网络中各种问题,快速定位故障有着非常重要的作用和意义。

       为此,我根据W.Richard Stevens的《TCP/IP详解》一书,将这5个状态制成一个时序图(其实原书也有这个图,但不是很好理解),其中黑色字体来表示应用程序行为,红色字体是操作系统行为。

TCP状态转换图

=============================================================

===========================================================

      深刻理解上面两幅图对于编写基于TCP/IP的应用直观重要,尤其是在链接不正常时,非常有助于定位问题所在。下面给出一些常见问题和解答。

 

 

netstat -na -P tcp-f inet | grep TIME_WAIT

--------------------------------------------------------------------------

 

不要设置tcpb_time_wait_expire成零,只要是一个很小的值就可以了。这里必须同

时设置tcpb_time_wait_expiretcpb_state,只设置其中一个达不到效果。

 

利用adbTCPS_ESTABLISHED变为TCPS_CLOSE_WAIT,可以使一条TCP连接不再工作, 但这条连接并未销毁,tcpb_t结构也未删除。

 

利用adbTCPS_ESTABLISHED变为TCPS_CLOSED,会导致整个操作系统崩溃。可能是下 tcpb_t结构被删除,而上层socket并不了解,出现非法指针。

 

简化一下kill_timewait.sh

 

--------------------------------------------------------------------------

#! /sbin/sh

 

ndd /dev/tcptcp_status | nawk '{print $1 " " $2 " " $16 $17 " " $18}' | egrep 'TIME_WAIT'

 

echo

/usr/bin/echo'TCPB address to terminate: /c'

read tcpb_addr

echo

 

adb -kw /dev/ksyms/dev/mem << NSFOCUS_EOF

$tcpb_addr+0x30/Z0t6

$tcpb_addr+0x40/W-6

/$q

NSFOCUS_EOF

--------------------------------------------------------------------------

 

还可以写一个脚本自动清除所有TIME_WAIT状态TCP连接

 

--------------------------------------------------------------------------

#! /sbin/sh

 

ndd /dev/tcptcp_status | nawk '{print $1 " " $2 " " $16 $17 " " $18}' | /

egrep 'TIME_WAIT'| cut -d' ' -f1 | while read tcpb_addr

do

adb -kw /dev/ksyms/dev/mem << NSFOCUS_EOF

$tcpb_addr+0x30/Z0t6

$tcpb_addr+0x40/W-6

/$q

NSFOCUS_EOF

Done

 


=========================================                                     

Q: 我正在写一个unix server程序,不是daemon,经常需要在命令行上重启它,绝大多数时候工作正常,但是某些时候会报告"bind: address in use",于是重启失 败。

A: Andrew Gierth
server
程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。至于 TIME_WAIT状态,你无法避免,那是TCP协议的一部分。

Q:
如何避免等待60秒之后才能重启服务

A: Erik Max Francis

使用setsockopt,比如

--------------------------------------------------------------------------
int option = 1;

if ( setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option,
sizeof( option ) ) < 0 )
{
die( "setsockopt" );
}
--------------------------------------------------------------------------

Q:
编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?

A:
这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,指明"地址已经使用中"如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时 SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期 望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不 可能。

一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端
口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组 还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使用 SO_REUSEADDR 选项。

Q:
在客户机/服务器编程中(TCP/SOCK_STREAM),如何理解TCP自动机 TIME_WAIT 状态?

A: W. Richard Stevens <1999
年逝世,享年49>

下面我来解释一下 TIME_WAIT 状态,这些在<> 2.6节解释很清楚了。

MSL(
最大分段生存期)指明TCP报文在Internet上最长生存时间,每个具体的TCP实现 都必须选择一个确定的MSL值。RFC 1122建议是2分钟,但BSD传统实现采用了30秒。TIME_WAIT 状态最大保持时间是2 * MSL,也就是1-4分钟。 IP头部有一个TTL,最大值255。尽管TTL的单位不是秒(根本和时间无关),我们仍需 假设,TTL255TCP报文在Internet上生存时间不能超过MSL

TCP
报文在传送过程中可能因为路由故障被迫缓冲延迟、选择非最优路径等等,结果发送方TCP机制开始超时重传。前一个TCP报文可以称为"漫游TCP重复报文",后一个TCP报文可以称为"超时重传TCP重复报文",作为面向连接的可靠协议,TCP实现必须正确处理这种重复报文,因为二者可能最终都到达。

一个通常的TCP连接终止可以用图描述如下:

client server
FIN M
close -----------------> (
被动关闭)
ACK M+1
<-----------------
FIN N
<----------------- close
ACK N+1
----------------->

为什么需要 TIME_WAIT 状态?

假设最终的ACK丢失server重发FINclient必须维护TCP状态信息以便可以重发最终的ACK否则会发送RST,结果server认为发生错误。TCP实现必须可靠地终止连接的两个方向(全双工关闭)client必须进TIME_WAIT 状态,因为client可能面临重发最终ACK的情形。

{
scz 2001-08-31 13:28
先调用close()的一方会进入TIME_WAIT状态
}

此外,考虑一种情况,TCP实现可能面临先后两个同样的相关五元组。如果前一个连接处在 TIME_WAIT 状态,而允许另一个拥有相同相关五元组的连接出现,可能处理TCP报文时,两个连接互相干扰。使用 SO_REUSEADDR 选项就需要考虑这种情况。

为什么 TIME_WAIT 状态需要保持 2MSL 这么长的时间?

如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。 第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达,干扰了第二个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么完全响应完毕,要么被丢弃。建立第二个连接的时候,不会混淆。

A:
小四

Solaris 7下有内核参数对应 TIME_WAIT 状态保持时间

# ndd -get /dev/tcp tcp_time_wait_interval
240000
# ndd -set /dev/tcp tcp_time_wait_interval 1000

缺省设置是240000ms,也就是4分钟。如果用ndd修改这个值,最小只能设置到1000ms
也就是1秒。显然内核做了限制,需要Kernel Hacking

# echo "tcp_param_arr/W 0t0" | adb -kw /dev/ksyms /dev/mem
physmem 3b72
tcp_param_arr: 0x3e8 = 0x0
# ndd -set /dev/tcp tcp_time_wait_interval 0

我不知道这样做有什么灾难性后果,参看<>的声明。

Q: TIME_WAIT
状态保持时间为0会有什么灾难性后果?在普遍的现实应用中,好象也就是服务器不稳定点,不见得有什么灾难性后果吧?

D: rain@bbs.whnet.edu.cn

Linux
内核源码 /usr/src/linux/include/net/tcp.h

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully
* close the socket, about 60 seconds */

最好不要改为0,改成1。端口分配是从上一次分配的端口号+1开始分配的,所以一般 不会有什么问题。端口分配算法在tcp_ipv4.ctcp_v4_get_port中。

 

 

监听地址问题

[root@localhosti386]# netstat -nlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address        ForeignAddress             State      PID/Program name   
tcp        0      0 0.0.0.0:3306               0.0.0.0:*                  LISTEN      2804/mysqld        
tcp        0      0222.230.14.16:80          0.0.0.0:*                 LISTEN      4433/(squid)       
tcp        0      0127.0.0.1:80               0.0.0.0:*                  LISTEN      4350/httpd        
tcp        0      0 127.0.0.1:2006             0.0.0.0:*                  LISTEN     27724/php           
tcp        0      00.0.0.0:25                  0.0.0.0:*                 LISTEN      26234/master       
tcp        0      00.0.0.0:443                 0.0.0.0:*                  LISTEN     27724/php           
tcp        0      0:::22                         :::*                      LISTEN      2674/sshd          
udp        0      00.0.0.0:16384             0.0.0.0:*                                4433/(squid)       
udp        0      00.0.0.0:3130              0.0.0.0:*                                 4433/(squid)      

我说的本地地址的四种类型:
0 0.0.0.0
222.230.14.16
127.0.0.1
:::
都是本地地址为何要有这四种类型呢?都是代表什么含义和作用呢?

 

一般 0.0.0.0 表示 all or any address, listen 0.0.0.0 來說, 表示 listen 主机所有 interface ip 位址.

至於你說的 222.230.14.16 or 127.0.0.1 那都只是表示 listen 特定 ip 位址而已..

::: 那表示 listen 主机所有interface ipv6 位址.

 

 

网络字节序问题

低地址 。。。。。。。。高地址
04     03     02     01    
------》方法1
01     02     03     04    
------》方法2

上面是整数0x01020304的两种表示方法,其中方法1和方法2的区别就是高位放到高地址还是低地址。

方法叫做小端格式,方法2叫做大端格式,网络上使用的大端格式,而主机格式随着不同的机器不同,为了使得不同的主机格式能够无歧义的和网络格式相互赋值,一般牵涉到网络的开发库会定义一套两种格式之间的转换函数,这样直接使用转换函数就可以完成两者之间的转换。

例如在windows中有htonsntohshtonlntohl等一套函数,分别用来完成2个字节和4个字节的转换。

 

原创粉丝点击