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状态。

  • 相关阅读:
    Serialize and Deserialize Binary Tree
    sliding window substring problem汇总贴
    10. Regular Expression Matching
    《深入理解计算机系统》(CSAPP)读书笔记 —— 第七章 链接
    程序员如何写一份合格的简历?(附简历模版)
    9个提高代码运行效率的小技巧你知道几个?
    《深入理解计算机系统》(CSAPP)读书笔记 —— 第六章 存储器层次结构
    24张图7000字详解计算机中的高速缓存
    《深入理解计算机系统》(CSAPP)实验四 —— Attack Lab
    《深入理解计算机系统》(CSAPP)读书笔记 —— 第五章 优化程序性能
  • 原文地址:https://www.cnblogs.com/wilber2013/p/4847055.html
Copyright © 2011-2022 走看看