动手学习TCP:数据传输(转)
前面的文章介绍了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<TcpOption> 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"状态下,客户端收到的来自服务端的字节数。
通过Wireshark抓包可以看到,在连接建立请求包[SYN]中增加了MSS的设置,并且以后的数据传出中,TCP数据包的payload长度最大就是MSS的值。
下面就开始介绍上面实验中涉及的TCP数据传输的知识点。
TCP分段和IP分片
在网络上传输的数据包是有大小限制,这里就需要知道TCP分段和IP分片的概念了。
跟这两个概念紧密相关的就是MSS(Maximum Segment Size)和MTU(Maximum Transmission Unit)这两个指标了,这两个指标的值大小直接决定了TCP分段和IP分片。
下面分别看看MSS和MTU。
MTU
首先来看看MTU。
以太网和802.3对数据帧的长度都有一个限制,最大值分别是1500和1492个字节。链路层的这个指标称作MTU(注意MTU是链路层的概念),不同类型的网络大多数都有一个上限。
如果网络层(IP层)有一个数据报需要传输,且数据的长度比链路层的 MTU还大,那么网络层(IP层)就要进行分片(fragmentation),把数据报分成若干片,保证每一个分片都小于MTU;目的端的网络层(IP层)会对收到的分片进行重新组装。
也就是说,分片和重新组装过程发生在网络层(IP层),所以对运输层(TCP/UDP)是透明的。
下面看看通过ping命令演示IP分片,ping命令属于ICMP(Internet Control Messages Protocol)协议:
Wireshark的结果为下,这5000个字节的数据被分别放在了四个IP分片中,每个分片(最后一个分片除外)中的数据长度等于1480(1500 – 20[IP header]):
IP分片的问题:IP分片有一个很大的问题,由于IP层本身没有超时重传机制,即使只丢失一片数据也要重新传整个数据报。也就是说,对于上面截图中的4个Frame,任何一个丢失了,另外3个都需要进行重传。
使用UDP和ICMP的时候很容易导致IP分片,因为UDP和ICMP是不考虑MTU和分片的,而是把这些工作都丢给了网络层(IP层);但是,为了减少IP分片对TCP的影响,在TCP中提出了MSS来试图避免IP分片。
MSS
MSS就是TCP数据包每次能够传输的最大数据分段。
为了达到最佳的传输效能TCP协议在建立连接的时候通常要协商双方的MSS值,这个值TCP协议在实现的时候往往用MTU值代替(需要减去IP数据包首部的大小20Bytes和TCP数据段的首部20Bytes),所以往往MSS为1460。通讯双方会根据双方提供的MSS值得最小值确定为这次连接的最大MSS值。
回到本文开始的例子,在建立TCP连接的时候,客户端指定了MSS为800,服务端指定的MSS为1460。经过协商后,双方采用了较小的MSS,所以以后的数据包长度最到为800字节。TCP就是通过这种方式来避免IP分片的。
再看一个MSS的例子,通过Wireshark抓取了一段HTTP请求,通过GET方法请求jquery的一组数据包。
通过下面可以看到,当应用层有一个超过MSS的数据需要发送的时候,TCP会把应用层的数据分成多个TCP分段然后发送出去。每一个分段包都包含TCP首部,然后传递给网络层进一步增加IP首部。
区别
通过上面可以看到TCP分段和IP分片有下面的主要区别:
- TCP分段发生在传输层,分段的依据是MSS;IP分片发生在网络层,分片的依据是MTU
- TCP分段是在传输层完成,并在传输层进行重组;IP分片由网络层完成,也在网络层进行重组
再看Seq和Ack号
TCP传输的可靠性是通过Seq和Ack号来进行保证的,所以可以看出Seq和Ack号的重要性。
文章开始的实验中,另一个需要注意的地方就是Seq和Ack号的变化。
在前面TCP连接的相关文章中已经介绍了连接建立和终止时候Seq和Ack号的变化,可以总结得到下面公式:
确认包的Ack = 待确认包(特殊标志包)的Seq + 1
从Wireshark的截图中可以看到在数据传输中Seq和Ack号的变化。
对于数据包的确认,可以使用下面的方式进行计算:
确认包的Ack = 待确认数据包的Seq + 待确认数据包的数据长度(Len)
关于TCP半连接
在介绍TCP终止连接的时候,提到了由于TCP是全双工的,所以需要经过四次挥手才能关闭TCP连接。
TCP中有一个半连接的概念,就是TCP连接的一端在结束它的发送后,还能接收来自另一端数据。
还是回到文章开始的例子,服务端发出了终止TCP连接的请求[FIN, ACK],客户端进行了确认,到此服务端到客户端方向的TCP连接就关闭了。
但是,随后客户端向服务端发送了一段长度为480字节的数据,然后才关闭客户端到服务端方向的TCP连接。
总结
本文主要介绍了TCP数据传输中的几个重要的概念。
- MSS(Maximum Segment Size)
- Seq号和Ack号
- TCP半连接
通过这篇文章,一定能很好的认识TCP分段和IP分片的区别,以及MSS和MTU的关系
http://www.cnblogs.com/wilber2013/p/4853674.html
动手学习TCP:数据传输(转)的更多相关文章
- 动手学习TCP:数据传输
前面的文章介绍了TCP状态变迁,以及TCP状态变迁图中的一些特殊状态. 本文主要看看TCP数据传输过程中需要了解的一些重要点: MSS(Maximum Segment Size) Seq号和Ack号的 ...
- 动手学习TCP:总结和索引
TCP是一个十分复杂的协议,通过前面几篇文章只涉及了TCP协议中一些基本的概念. 虽然说都是一些TCP最基本的概念,但是试验过程中一直在踩坑,例如:TCP flag设置错误,seq.ack号没有计算正 ...
- 动手学习TCP:4种定时器
上一篇中介绍了TCP数据传输中涉及的一些基本知识点.本文让我们看看TCP中的4种定时器. TCP定时器 对于每个TCP连接,TCP管理4个不同的定时器,下面看看对4种定时器的简单介绍. 重传定时器使用 ...
- 动手学习TCP:TCP特殊状态
前面两篇文章介绍了TCP状态变迁,以及通过实验演示了客户端和服务端的正常状态变迁. 下面就来看看TCP状态变迁过程中的几个特殊状态. SYN_RCVD 在TCP连接建立的过程中,当服务端接收到[SYN ...
- 动手学习TCP: 环境搭建
前一段时间通过Wireshark抓包,定位了一个客户端和服务器之间数据传输的问题.最近就抽空看了看<TCP/IP详解 卷1>中关于TCP的部分,书中用了很多例子展示了TCP/IP协议中的一 ...
- 动手学习TCP:TCP连接建立与终止
TCP是一个面向连接的协议,任何一方在发送数据之前,都必须先在双方之间建立一条连接.所以,本文就主要看看TCP连接的建立和终止. 在开始介绍TCP连接之前,先来看看TCP数据包的首部,首部里面有很多重 ...
- 动手学习TCP:客户端状态变迁
上一篇文章中介绍了TCP连接的建立和终止. 通过实际操作了解到,在TCP协议工作过程中,客户端和服务端都会接收或者发送特定标志的TCP数据包,然后进入不同的状态. 也就是说,TCP协议就是一个包含多种 ...
- 动手学习TCP:服务端状态变迁
上一篇文章介绍了TCP状态机,并且通过实验了解了TCP客户端正常的状态变迁过程. 那么,本篇文章就一起看看TCP服务端的正常状态变迁过程 服务端状态变迁 根据上一篇文章中的TCP状态变迁图,可以得到服 ...
- [转] 用协议分析工具学习TCP/IP
一.前言 目前,网络的速度发展非常快,学习网络的人也越来越多,稍有网络常识的人都知道TCP/IP协议是网络的基础,是Internet的语言,可以说没有TCP/IP协议就没有互联网的今天.目前号称搞网的 ...
随机推荐
- Word文件交换的电脑打开字体、排版变化的原因和解决方法!
方案: 有时候.我们好不easy用Word写好文档,做好排版发给别人,别人会告诉你格式怎么是乱的啊,标题.正文.页眉页脚什么的格式都变了. 想尽各种办法都没能得到解决,那么出现这样的情况的原因究竟是什 ...
- DataGridView大扩展——显示行号
原文 DataGridView大扩展——显示行号 在DataGridView 的实际使用中,经常需要标示出行号,这样可以比较醒目地看到当前信息.不过DataGridView 在绘制 DataGridV ...
- linux挂载U盘,及乱码问题解决
1. 首先使用切换到root用户. 2. 使用fdisk -l命令查看磁盘信息,找到u盘(能够依据显示的大小确定) 3. 在/mnt下创建挂载点,比如创建usb目录:mkdir /mnt/usb 4. ...
- Shell在大数据的魅力时代:从一点点思路百度大数据面试题
供Linux开发中的同学们,Shell这可以说是一个基本功. 对于同学们的操作和维护.Shell也可以说是一种必要的技能,Shell.对于Release Team,软件配置管理的同学来说.Shell也 ...
- Linux curl使用简单介绍 (转)
Curl是Linux下一个很强大的http命令行工具,其功能十分强大. 1) 二话不说,先从这里开始吧! $ curl http://www.linuxidc.com 回车之后,www.linuxid ...
- 360的IM可能会是什么样?
尽管周鸿祎不止一次的说过,它不会进入IM领域,可是在360和QQ大战之后,很多用户 在卸载了QQ,寻找替代的IM工具的时候,发现他们非常难找到合适的替代IM工具,由于 好友非常难迁移.在用户的强烈 呼 ...
- C#如何获得 WINDOWS
版本号
using System; using System.Runtime.InteropServices; namespace GetWindowsVersion { [ StructLayout( La ...
- [LeetCode257]Binary Tree Paths
题目: Given a binary tree, return all root-to-leaf paths. For example, given the following binary tree ...
- C#中的预编译指令介绍
原文:C#中的预编译指令介绍 1.#define和#undef 用法: #define DEBUG #undef DEBUG #define告诉编译器,我定义了一个DEBUG的一个符号,他类似一个变量 ...
- SQLServer2008-2012资源及性能监控—CPU使用率监控具体解释
前言: CPU是server中最重要的资源.在数据库server中,CPU的使用情况应该时刻监控以便SQLServer一直处于最佳状态. 本文将会使用可靠性和性能监视器来获取CPU相关的使用统计信息 ...