上一篇文章介绍了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
)) // 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 = ;
endPointInfo.DestinationPort = ; 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状态。

动手学习TCP:服务端状态变迁的更多相关文章

  1. 动手学习TCP:客户端状态变迁

    上一篇文章中介绍了TCP连接的建立和终止. 通过实际操作了解到,在TCP协议工作过程中,客户端和服务端都会接收或者发送特定标志的TCP数据包,然后进入不同的状态. 也就是说,TCP协议就是一个包含多种 ...

  2. 动手学习TCP:总结和索引

    TCP是一个十分复杂的协议,通过前面几篇文章只涉及了TCP协议中一些基本的概念. 虽然说都是一些TCP最基本的概念,但是试验过程中一直在踩坑,例如:TCP flag设置错误,seq.ack号没有计算正 ...

  3. 动手学习TCP:数据传输

    前面的文章介绍了TCP状态变迁,以及TCP状态变迁图中的一些特殊状态. 本文主要看看TCP数据传输过程中需要了解的一些重要点: MSS(Maximum Segment Size) Seq号和Ack号的 ...

  4. 动手学习TCP:数据传输(转)

    前面的文章介绍了TCP状态变迁,以及TCP状态变迁图中的一些特殊状态. 本文主要看看TCP数据传输过程中需要了解的一些重要点: MSS(Maximum Segment Size) Seq号和Ack号的 ...

  5. 动手学习TCP:TCP特殊状态

    前面两篇文章介绍了TCP状态变迁,以及通过实验演示了客户端和服务端的正常状态变迁. 下面就来看看TCP状态变迁过程中的几个特殊状态. SYN_RCVD 在TCP连接建立的过程中,当服务端接收到[SYN ...

  6. 动手学习TCP: 环境搭建

    前一段时间通过Wireshark抓包,定位了一个客户端和服务器之间数据传输的问题.最近就抽空看了看<TCP/IP详解 卷1>中关于TCP的部分,书中用了很多例子展示了TCP/IP协议中的一 ...

  7. 动手学习TCP:4种定时器

    上一篇中介绍了TCP数据传输中涉及的一些基本知识点.本文让我们看看TCP中的4种定时器. TCP定时器 对于每个TCP连接,TCP管理4个不同的定时器,下面看看对4种定时器的简单介绍. 重传定时器使用 ...

  8. C++封装的基于WinSock2的TCP服务端、客户端

    无聊研究Winsock套接字编程,用原生的C语言接口写出来的代码看着难受,于是自己简单用C++封装一下,把思路过程理清,方便自己后续翻看和新手学习. 只写好了TCP通信服务端,有空把客户端流程也封装一 ...

  9. 利用select实现IO多路复用TCP服务端

    一.相关函数 1.  int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeva ...

随机推荐

  1. 在MVC中应用百度富文本编辑器

    1.下载.NET版本的百度富文本编辑器,前往 下载.NET版本百度富文本框 2.解压下载的.zip压缩包,将utf8-.net文件夹名称改为:ueditor,复制到MVC根目录下面.结构如下: App ...

  2. 跟踪js文件作为iframe页面不起作用时(淘宝天猫)

    跟踪文件 (function(win, doc) { var s = doc.createElement("script"), h = doc.getElementsByTagNa ...

  3. js事件小记

    参考javascript编程全解  javascript高级程序设计 javascript经典实例 对事件的处理方式称为事件处理程序或事件侦听器 ,对于一个元素或事件,只能设定1个事件处理程序,却可以 ...

  4. rails provide与content_for的区别

    页面渲染时:provide先执行,但找到一个provide之后就不再查找 content_for 顺序执行,在哪个位置,就等之前的渲染完后才执行.但是要等到所有的content被查找完后一块返回,也就 ...

  5. VS2010中重命名项目

    通常,在项目开发初始,一般都是先做一个Demo,以方便进行修改设计.演示等,这可能导致项目命名并不符合最后的规范,则需要修改项目名称.VS没有推出这方面针对性的功能,网友们也纷纷自己写了软件. 方案一 ...

  6. 实践GTD三周年后的体会

    从2011年7月开始实践GTD到现在已经3年多了,按照年初设定的目标,应该写点GTD的体会,但提起笔来的时候却发现并没有什么太新的体会,大部分体会都已经在以前的文章中写到了,甚至有些流程已经简化了,看 ...

  7. OC中几种集合的遍历方法(数组遍历,字典遍历,集合遍历)

    // 先分别初始化数组.字典和集合,然后分别用for循环.NSEnumerator枚举器和forin循环这三个方法来实现遍历 NSArray *array = @[@"yinhao" ...

  8. Python学习一入门

    一.打印Hello和多行文本 print 打印 后跟单引号或者双引号 多行:3个单引号或者3个双引号 二.算术运算 2.1.加减乖法 默认1/2=0 如果需要小数运算,则需要一个运算术上加.或者.0 ...

  9. oc 字符串

    #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { ...

  10. Swift语言

    本文为熟悉使用Objective-C开发iOS快速转入swift开发提供一些便利的途径,没有过多华丽的说明底层原理,也没有过多的概念说明,只提供swift的基本使用以及一些使用示例.高手请让路哈. S ...