动手学习TCP系列之TCP连接建立与终止(1)(2)
连接终止注意点
在建立TCP连接的过程中,有以下两点需要注意一下:
[FIN]标志的数据包会使用消耗一个序号,所以对端的确认号(ack)是当前序号(seq)加一
与建立连接时的三次 握手不同,终止连接需要四次挥手
因为TCP连接是全双工的,每个方向都必须单独进行关闭。当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,但是TCP连接在收到一个FIN后仍能发送数据
TCP连接实验
好了,了解了TCP连接建立和终止的基本知识后,就可以通过Pcap.Net来进行TCP连接建立和终止的实验了。
建立连接代码的基本流程如下:
client程序使用一个初始的seq序号(100),然后生成并发送一个带[SYN]标志的TCP包
client将期待来自服务端的[SYN, ACK]包
当收到[SYN, ACK]包之后,client需要生成并发送一个[ACK]包进行确认,这个[ACK]包的ack号是[SYN, ACK]包seq号加一
终止连接代码的基本流程如下:
client程序delay 10秒钟,然后发送[FIN, ACK]包关闭client到server的通路,继续使用全局的seq号
client将期待来自服务端的[ACK]包,以及[FIN, ACK]包
最后client发送[ACK]包,seq号需要加一,因为[FIN]标志的包将消耗一个序号,TCP连接终止完成
主程序如下,发送TCP连接建立和终止请求,每个请求发送后都用PacketHandler处理收到的包:
communicator.SendPacket(Utils.BuildTcpPacket(endPointInfo, TcpControlBits.Synchronize, null)); PacketHandler(communicator, endPointInfo); // delay 10 secs, then client to send Fin Thread.Sleep(10000); communicator.SendPacket(Utils.BuildTcpPacket(endPointInfo, TcpControlBits.Fin | TcpControlBits.Acknowledgment)); PacketHandler(communicator, endPointInfo);
程序的主要逻辑在PacketHandler中,这个函数根据收到的不同TCP包的类型完成不同的逻辑,产生并发送不同类型的包。
例如,当PacketHandler接收到来自服务端的[SYN, ACK]包后,处理函数就会生成并发送一个[ACK]确认包。也就是说,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 | TcpControlBits.Acknowledgment): Utils.PacketInfoPrinter(packet); Packet ack4SynAck = Utils.BuildTcpResponsePacket(packet, TcpControlBits.Acknowledgment); communicator.SendPacket(ack4SynAck); break; case (TcpControlBits.Fin | TcpControlBits.Acknowledgment): Utils.PacketInfoPrinter(packet); Packet ack4FinAck = Utils.BuildTcpResponsePacket(packet, TcpControlBits.Acknowledgment); communicator.SendPacket(ack4FinAck); break; case TcpControlBits.Acknowledgment: Utils.PacketInfoPrinter(packet); break; default: Utils.PacketInfoPrinter(packet); break; } } else { switch (packet.Ethernet.IpV4.Tcp.ControlBits) { case (TcpControlBits.Fin | TcpControlBits.Acknowledgment): Utils.PacketInfoPrinter(packet); break; case TcpControlBits.Synchronize: Utils.PacketInfoPrinter(packet); break; case TcpControlBits.Acknowledgment: Utils.PacketInfoPrinter(packet); running = false; break; default: Utils.PacketInfoPrinter(packet); break; } } break; default: throw new InvalidOperationException("The result " + result + " should never be reached here"); } } while (running); }
评论暂时关闭