动手学习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, List tcpOptionList = 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"状态下,客户端收到的来自服务端的字节数。

 




相关内容