• TCP状态图和常见问题


    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_expire tcpb_state ,只设置其中一个达不到效果。

     

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

     

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

     

    简化一下 kill_timewait.sh

     

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

    #! /sbin/sh

     

    ndd /dev/tcp tcp_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/Z 0t6

    $tcpb_addr+0x40/W -6

    \$q

    NSFOCUS_EOF

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

     

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

     

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

    #! /sbin/sh

     

    ndd /dev/tcp tcp_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/Z 0t6

    $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 的单位不是秒 ( 根本和时间无关 ) ,我们仍需 假设, TTL 255 TCP 报文在 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 重发 FIN client 必须维护 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.c tcp_v4_get_port 中。

     

     

    监听地址问题

    [root@localhost i386]# netstat -nlp
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address        Foreign Address             State       PID/Program name   
    tcp        0      0 0.0.0.0:3306                0.0.0.0:*                   LISTEN      2804/mysqld         
    tcp        0      0 222.230.14.16:80          0.0.0.0:*                   LISTEN      4433/(squid)        
    tcp        0      0 127.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      0 0.0.0.0:25                    0.0.0.0:*                   LISTEN      26234/master        
    tcp        0      0 0.0.0.0:443                  0.0.0.0:*                   LISTEN      27724/php           
    tcp        0      0 :::22                           :::*                        LISTEN      2674/sshd           
    udp        0      0 0.0.0.0:16384              0.0.0.0:*                                  4433/(squid)        
    udp        0      0 0.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 中有 htons ntohs htonl ntohl 等一套函数,分别用来完成 2 个字节和 4 个字节的转换。

     

  • 相关阅读:
    论自己电脑如何搭建服务器
    nodejs + express + art-template + mongodb简单项目
    npm和yarn使用
    Linux内核编译
    Linux 网络编程
    Linux进程管理
    LeetCode1576. 替换所有的问号
    LeetCode392. 判断子序列
    LeetCode674. 最长连续递增序列
    剑指 Offer 48. 最长不含重复字符的子字符串
  • 原文地址:https://www.cnblogs.com/zhenjing/p/2021791.html
Copyright © 2020-2023  润新知