zoukankan      html  css  js  c++  java
  • 动手学习TCP:服务端状态变迁

    上一篇文章介绍了TCP状态机,并且通过实验了解了TCP客户端正常的状态变迁过程。

    那么,本篇文章就一起看看TCP服务端的正常状态变迁过程

    服务端状态变迁

    根据上一篇文章中的TCP状态变迁图,可以得到服务器的正常状态变迁流程如下:

    CLOSED -> LISTEN -> SYN_RECV -> ESTABLISHED -> CLOSE_WAIT -> LAST_ACK -> CLOSED

    具体的将状态跟TCP包关联起来就如下表示:

    From State

    To State

    Recv Packet

    Send Packet

    CLOSED

    LISTEN

    -

    -

    LISTEN

    SYN_RCVD

    [SYN]

    [SYN, ACK]

    SYN_RCVD

    ESTABLISHED

    [ACK]

    -

    ESTABLISHED

    CLOSE_WAIT

    [FIN, ACK]

    [ACK]

    CLOSE_WAIT

    LAST_ACK

    -

    [FIN, ACK]

    LAST_ACK

    CLOSED

    [ACK]

    -

    服务端状态变迁实验

    下面就结合上面分析出来的服务端状态变迁表,利用Pcap.Net来模拟服务端正常的状态变迁过程。

    代码实现

    跟前面几次正好相反,这次我们将在宿主机运行Pcap.Net实现的服务端,然后在虚拟机运行一个客户端。

    对于服务端,主程序中设置了源和目的端的连接信息,这次宿主机中的服务端将监听“3333”端口。

    然后,程序中设置了服务端TCP初始状态为"LISTENING",然后就直接运行监听函数了。

    // Open the output device
    using (PacketCommunicator communicator = selectedDevice.Open(System.Int32.MaxValue, // name of the device
                                                                 PacketDeviceOpenAttributes.Promiscuous, // promiscuous mode
                                                                 1)) // read timeout
    {
        EndPointInfo endPointInfo = new EndPointInfo();
        endPointInfo.SourceMac = "08:00:27:00:C0:D5";
        endPointInfo.DestinationMac = "";
        endPointInfo.SourceIp = "192.168.56.101";
        endPointInfo.DestinationIp = "";
        endPointInfo.SourcePort = 3333;
        endPointInfo.DestinationPort = 0;
    
        using (BerkeleyPacketFilter filter = communicator.CreateFilter("tcp port " + endPointInfo.SourcePort))
        {
            // Set the filter
            communicator.SetFilter(filter);
        }
    
        tcpStatus = TCPStatus.LISTENING;
        PacketHandler(communicator, endPointInfo);
    }

    这次的监听函数"PacketHandler"中的逻辑,跟上一次客户端的例子还是有很大差别的。

    首先是期待接收和实际发送的TCP包类型有很大的差别,其次就是状态之间的变迁是完全不同的。但是,代码的逻辑依然是根据上面的服务端状态变迁表。

    private static void PacketHandler(PacketCommunicator communicator, EndPointInfo endPointInfo)
    {
        Packet packet = null;
        bool running = true;
    
        do
        {
            PacketCommunicatorReceiveResult result = communicator.ReceivePacket(out packet);
    
            switch (result)
            {
                case PacketCommunicatorReceiveResult.Timeout:
                    // Timeout elapsed
                    continue;
                case PacketCommunicatorReceiveResult.Ok:
                    bool isRecvedPacket = (packet.Ethernet.IpV4.Destination.ToString() == endPointInfo.SourceIp) ? true : false;
    
                    if (isRecvedPacket)
                    {
                        switch (packet.Ethernet.IpV4.Tcp.ControlBits)
                        {
                            case TcpControlBits.Synchronize:
                                if (tcpStatus == TCPStatus.LISTENING)
                                {
                                    endPointInfo.DestinationMac = packet.Ethernet.Source.ToString();
                                    endPointInfo.DestinationIp = packet.Ethernet.IpV4.Source.ToString();
                                    endPointInfo.DestinationPort = packet.Ethernet.IpV4.Tcp.SourcePort;
    
                                    Utils.PacketInfoPrinter(packet);
                                    Packet synAck = Utils.BuildTcpResponsePacket(packet, TcpControlBits.Synchronize | TcpControlBits.Acknowledgment);
                                    communicator.SendPacket(synAck);
                                    tcpStatus = TCPStatus.SYN_RECEIVED;
                                }
                                break;
                            case TcpControlBits.Acknowledgment:
                                if (tcpStatus == TCPStatus.SYN_RECEIVED)
                                {
                                    tcpStatus = TCPStatus.ESTABLISHED;
                                    Utils.PacketInfoPrinter(packet, tcpStatus);
                                }
                                else if (tcpStatus == TCPStatus.LAST_ACK)
                                {
                                    tcpStatus = TCPStatus.CLOSED;
                                    Utils.PacketInfoPrinter(packet, tcpStatus);
                                    tcpStatus = TCPStatus.LISTENING;
                                }
                                else if (tcpStatus == TCPStatus.FIN_WAIT_1)
                                {
                                    tcpStatus = TCPStatus.FIN_WAIT_2;
                                    Utils.PacketInfoPrinter(packet);
                                }
                                break;
                            case (TcpControlBits.Fin | TcpControlBits.Acknowledgment):
                                if (tcpStatus == TCPStatus.FIN_WAIT_2)
                                {
                                    Utils.PacketInfoPrinter(packet);
                                    Packet ack = Utils.BuildTcpResponsePacket(packet, TcpControlBits.Acknowledgment);
                                    communicator.SendPacket(ack);
                                    tcpStatus = TCPStatus.TIME_WAIT;
                                }
                                else if (tcpStatus == TCPStatus.ESTABLISHED)
                                {
                                    Utils.PacketInfoPrinter(packet);
                                    Packet ack = Utils.BuildTcpResponsePacket(packet, TcpControlBits.Acknowledgment);
                                    communicator.SendPacket(ack);
                                    tcpStatus = TCPStatus.CLOSE_WAIT;
                                }
                                break;
                            default:
                                Utils.PacketInfoPrinter(packet);
                                break;
                        }
                    }
                    else
                    {
                        switch (packet.Ethernet.IpV4.Tcp.ControlBits)
                        {
                            case (TcpControlBits.Synchronize | TcpControlBits.Acknowledgment):
                                if (tcpStatus == TCPStatus.SYN_RECEIVED)
                                {
                                    Utils.PacketInfoPrinter(packet, tcpStatus);
                                }
                                break;
                            case (TcpControlBits.Fin | TcpControlBits.Acknowledgment):
                                if (tcpStatus == TCPStatus.FIN_WAIT_1)
                                {
                                    Utils.PacketInfoPrinter(packet, tcpStatus);
                                }
                                else if (tcpStatus == TCPStatus.LAST_ACK)
                                {
                                    Utils.PacketInfoPrinter(packet, tcpStatus);
                                }
                                break;
                            case TcpControlBits.Acknowledgment:
                                if (tcpStatus == TCPStatus.TIME_WAIT)
                                {
                                    Utils.PacketInfoPrinter(packet, tcpStatus);
                                }
                                else if (tcpStatus == TCPStatus.CLOSE_WAIT)
                                {
                                    
                                    Utils.PacketInfoPrinter(packet, tcpStatus);
    
                                    Packet fin = Utils.BuildTcpPacket(endPointInfo, TcpControlBits.Fin | TcpControlBits.Acknowledgment);
                                    communicator.SendPacket(fin);
                                    tcpStatus = TCPStatus.LAST_ACK;
                                }
                                break;
                            default:
                                Utils.PacketInfoPrinter(packet);
                                break;
                        }
                    }
                    break;
                default:
                    throw new InvalidOperationException("The result " + result + " should never be reached here");
            }
        } while (running);
    
    }

    对于客户端,通过Python实现了一个简单的Socket程序来模拟客户端行为:

    from socket import *
    import time
    
    HOST = "192.168.56.101"
    PORT = 3333
    BUFSIZ = 1024
    ADDR = (HOST, PORT)
    
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(ADDR)
    
    time.sleep(5)
    
    client.close()

    运行效果

    这次,宿主机上运行的是服务端,虚拟机运行的是客户端,打开Wireshark监听"VirtualBox Host-Only Network"网卡,并设置filter为"port 3333"。

    运行服务端程序,服务端将处于监听状态。这是在虚拟机中运行"client.py"。这时,通过服务端console可以看到客户端和服务端之间的包,以及服务端的状态变迁。

    Wireshark依然显示的是TCP连接建立和终止的过程。

    netstat命令

    netstat是控制台命令,是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。netstat用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。

    实验中的宿主机系统是Win7,下面看看通过 netatat /? 获得的帮助信息:

    netstat命令失效?

    虽然说上面的程序可以打印出服务端的状态变迁过程,但是这次让我们通过netstat命令查看一下。

    为了方便查看,将"client.py"中的"time.sleep(5)"改为"time.sleep(300)",使客户端跟服务器之间的连接保持300秒。客户端的端口号为"1090"。

    这时,分别在服务端和客户端cmd窗口中执行 netstat -anp TCP | findstr "192.168.56" 命令,查看包含"192.168.56"字符串的TCP连接:

    服务端:

    客户端:

    为什么服务端看不到TCP连接?就像我们第一篇介绍的那样,Pcap.Net是不经过操作系统协议栈的,所以这也就解释了为什么"netstat"命令发现不了服务端的TCP连接。

    等300秒结束后,客户端会发送终止连接请求。当连接终止后,可以看大客户端的TCP连接状态变成了"TIME_WAIT"。

    客户端:

    总结

    本文中根据TCP状态变迁图,得到了服务端的状态变迁表。

    然后使用Pcap.Net,基于服务端的状态变迁表,构建了一个简单的服务端,展示了服务端状态变迁的过程。

    文中还简单的介绍了"netstat"命令,通过这个命令可以查看TCP连接的状态,结合这个命令,可以更好的了解TCP状态。

  • 相关阅读:
    Swift
    遇到奇怪的问题,帮助威猛答案,表单提交的文件提交的无限数据问题
    对于思考小端和大端字节顺序
    圈真的决定你的未来?
    坦克大战
    android visible invisible和gone差异
    编程之美2: 二进制重建
    WCF分布式事务(EF)
    Android+NDK+OpenGLES开发环境配置
    struts2和struts1认识
  • 原文地址:https://www.cnblogs.com/wilber2013/p/4847055.html
Copyright © 2011-2022 走看看