动手学习TCP系列之数据传输(1)
动手学习TCP系列之数据传输(1)
前面的文章介绍了TCP状态变迁,以及TCP状态变迁图中的一些特殊状态。
本文主要看看TCP数据传输过程中需要了解的一些重要点:
MSS(Maximum Segment Size)
Seq号和Ack号的计算
TCP半连接
TCP数据传输实验
在开始介绍上面列出的内容之前,先看看实验程序的运行。
本文的例子代码是基于"动手学习TCP:客户端状态变迁"文章中的例子。
首先,修改了"BuildTcpPacket"这个函数,增加了两个功能:
正常情况下TCP首部是20个字节,但是TCP首部支持一些特殊"Options"(MSS就是其中一个);所以,第一个改动就是支持创建带特殊选项的TCP包
第二个改动是可以通过参数设置为TCP包增加Payload,这样就可以通过TCP包传输数据了。
public static Packet BuildTcpPacket(EndPointInfo endPointInfo, TcpControlBits tcpControlBits, ListtcpOptionList = null, bool withPayload = false, string payloadData = "") { EthernetLayer ethernetLayer = new EthernetLayer { Source = new MacAddress(endPointInfo.SourceMac), Destination = new MacAddress(endPointInfo.DestinationMac), EtherType = EthernetType.None, // Will be filled automatically. }; IpV4Layer ipV4Layer = new IpV4Layer { Source = new IpV4Address(endPointInfo.SourceIp), CurrentDestination = new IpV4Address(endPointInfo.DestinationIp), Fragmentation = IpV4Fragmentation.None, HeaderChecksum = null, // Will be filled automatically. Identification = 123, Options = IpV4Options.None, Protocol = null, // Will be filled automatically. Ttl = 10, TypeOfService = 0, }; TcpLayer tcpLayer = new TcpLayer { SourcePort = endPointInfo.SourcePort, DestinationPort = endPointInfo.DestinationPort, Checksum = null, // Will be filled automatically. SequenceNumber = seqNum, AcknowledgmentNumber = ackNum, ControlBits = tcpControlBits, Window = windowSize, UrgentPointer = 0, Options = (tcpOptionList == null) ? TcpOptions.None : new TcpOptions(tcpOptionList), }; PacketBuilder builder; if (withPayload) { PayloadLayer payloadLayer = new PayloadLayer { Data = new Datagram(System.Text.Encoding.ASCII.GetBytes(payloadData)), }; builder = new PacketBuilder(ethernetLayer, ipV4Layer, tcpLayer, payloadLayer); return builder.Build(DateTime.Now); } builder = new PacketBuilder(ethernetLayer, ipV4Layer, tcpLayer); return builder.Build(DateTime.Now); }
代码其余的改动发生在"PacketHandler"函数中:
private static void PacketHandler(PacketCommunicator communicator, EndPointInfo endPointInfo, bool clientToSendFin = true)
增加了对于"ESTABLISHED"状态下收到数据包的处理,主要作用就是发送一个[ACK]包对收到的数据包进行确认。
case TcpControlBits.Acknowledgment: if (tcpStatus == TCPStatus.FIN_WAIT_1) { tcpStatus = TCPStatus.FIN_WAIT_2; Utils.PacketInfoPrinter(packet, tcpStatus); } else if (tcpStatus == TCPStatus.LAST_ACK) { tcpStatus = TCPStatus.CLOSED; Utils.PacketInfoPrinter(packet, tcpStatus); running = false; } else if (tcpStatus == TCPStatus.ESTABLISHED) { //print the data received from server Console.WriteLine(packet.Ethernet.IpV4.Tcp.Payload.ToString()); communicator.SendPacket(Utils.BuildTcpResponsePacket(packet, TcpControlBits.Acknowledgment)); } break; case (TcpControlBits.Acknowledgment | TcpControlBits.Push): if (tcpStatus == TCPStatus.ESTABLISHED) { //print the data received from server Console.WriteLine(packet.Ethernet.IpV4.Tcp.Payload.ToString()); communicator.SendPacket(Utils.BuildTcpResponsePacket(packet, TcpControlBits.Acknowledgment)); } break;
运行效果
代码修改好之后,运行代码。
通过console端可以看到,在连接为"ESTABLISHED"状态下,客户端收到的来自服务端的字节数。
评论暂时关闭