文章目录
- TCP 协议
- 简介
- 数据包格式
- TCP的特性
- 连接机制
- 确认与重传
- 缓冲机制
- 全双工通信
- 流量控制
- 差错控制
- 拥塞控制
- 端口号
- 三次握手
- 数据传输
- 四次挥手
- 抓包
- 参考
本文为笔者学习以太网对网上资料归纳整理所做的笔记,文末均附有参考链接,如侵权,请联系删除。
TCP 协议
简介
TCP与UDP一样,都是传输层的协议,但是提供的服务却大不相同,UDP为上层应用提供的是一种不可靠的,无连接的服务,而TCP则提供一种面向连接、可靠的字节流传输服务,TCP让两个主机建立连接的关系,应用数据以数据流的形式进行传输,这与UDP协议是不一样:
UDP运载的数据是以报文的形式,各个报文在网络中互不相干传输,UDP每收到一个报文就递交给上层应用,因此如果对于大量数据来说,应用层的重装是非常麻烦的,因为UDP报文在网络中到达目标主机的顺序是不一样的;
而TCP采用数据流的形式传输,先后发出的数据在网络中虽然也是互不相干的传输,但是这些数据本身携带的信息却是紧密联系的,TCP协议会给每个传输的字节进行编号,当然啦,两个主机方向上的数据编号是彼此独立的,在传输的过程中,发送方把数据的起始编号与长度放在TCP报文中,在接收方将所有数据按照编号组装起来,然后返回一个确认,当所有数据接收完成后才将数据递交到应用层中。
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的协议,使用该协议时,可以保证客户端和服务端的连接是可靠和安全的。使用 TCP 协议进行通信之前,通信双方必须先建立连接,然后再进行数据传输,通信结束后终止连接。
- 优点:能保证可靠性、稳定性。
- 适用场景:TCP适合用于端到端的通信,适用于对可靠性要求较高的服务。
- 基于 TCP 的 socket 编程流程如下图所示:
数据包格式
TCP 把连接作为最基本的对象,每一条 TCP 连接都有两个端点,这种端点我们叫作套接字(socket),它的定义为端口号拼接到 IP 地址即构成了套接字,例如,若 IP 地址为 192.3.4.16 而端口号为 80,那么得到的套接字为192.3.4.16:80。IP 协议虽然能把数据报文送到目的主机,但是并没有交付给主机的具体应用进程。而端到端的通信才是应用进程之间的通信。
TCP 报文是 TCP 层传输的数据单元,也叫报文段。 TCP 报文的格式如下图所示
- 源端口号:每个TCP报文段都包含源主机和目标主机的端口号,用于寻找发送端和接收端应用线程,这两个值加上I P首部中的源I P地址和目标I P地址就能确定唯一一个TCP连接。
- 目标端口号:每个TCP报文段都包含源主机和目标主机的端口号,用于寻找发送端和接收端应用线程,这两个值加上I P首部中的源I P地址和目标I P地址就能确定唯一一个TCP连接。
- 序号:序号字段用来标识从 TCP 发送端向 TCP 接收端发送的数据字节流,它的值表示在这个报文段中的第一个数据字节所处位置码,根据接收到的数据区域长度,就能计算出报文最后一个数据所处的序号,因为TCP协议会对发送或者接收的数据进行编号(按字节的形式),那么使用序号对每个字节进行计数,就能很轻易管理这些数据。序号是32 bit的无符号整数。
- 确认序号:既然 TCP 协议给每个传输的字节都编了号,那么确认序号就包含接收端所期望收到的下一个序号,因此,确认序号应当是上次已成功收到数据的最后一个字节序号加 1。当然,只有ACK标志为 1时确认序号字段才有效,TCP为应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输,因此确认序号通常会与反向数据(即接收端传输给发送端的数据)封装在同一个报文中(即捎带),所以连接的每一端都必须保持每个方向上的传输数据序号准确性。
- 手部长度:首部长度字段占据4bit空间,它指出了TCP报文段首部长度,以字节为单位,最大能记录15*4=60字节的首部长度,因此,TCP报文段首部最大长度为60字节。在字段后接下来有6bit空间是保留未用的。
- 保留字段:6bit 保留字段
- 标志字段
- URG:紧急指针标志,为1时表示紧急指针有效,为0则忽略紧急指针。
- ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段。
- PSH:push标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。
- RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求。
- SYN:同步序号,用于建立连接过程,在连接请求中,SYN=1和ACK=0表示该数据段没有使用捎带的确认域,而连接应答捎带一个确认,即SYN=1和ACK=1。
- FIN:finish标志,用于释放连接,为1时表示发送方已经没有数据发送了,即关闭本方数据流。
- .窗口大小:TCP的流量控制由连接的每一端通过声明的窗口大小来提供,窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端正期望接收的数据序号,发送方根据窗口大小调整发送数据,以实现流量控制。窗口大小是一个占据16 bit空间的字段,因而窗口最大为 65535字节,当接收方告诉发送方一个大小为0的窗口时,将完全阻止发送方的数据发送。
- 检验和:校验和覆盖了整个的 TCP报文段:TCP 首部 和TCP 数据区域,由发送端计算和填写,并由接收端进行验证。
- 紧急指针:只有当URG标志置1时紧急指针才有效,紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。简单来说,本TCP报文段的紧急数据在报文段数据区域中,从序号字段开始,偏移紧急指针的值结束。
TCP的特性
连接机制
TCP是一个面向连接的协议,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一个连接,否则将无法发送数据,一个TCP连接必须有双方IP地址与端口号,就像打电话一样,必须知道双方的电话号码才会打电话,关于具体的连接我们在后文讲解。
确认与重传
一个完整的TCP传输必须有数据的交互,接收方在接收到数据之后必须正面进行确认,向发送方报告接收的结果,而发送方在发送数据之后必须等待接收方的确认,同时发送的时候会启动一个定时器,在指定超时时间内没收到确认,发送方就会认为发送失败,然后进行重发操作,这就是重传报文。
TCP提供可靠的运输层,但它依赖的是IP层的服务,IP数据报的传输是无连接、不可靠的,因此它要通过确认来知道接收方确实已经收到数据了。但数据和确认都有可能会丢失,因此TCP通过在发送时设置一个超时机制(定时器)来解决这种问题,如果当超时时间到达的时候还没有收到对方的确认,它就重传该数据。
缓冲机制
在发送方想要发送数据的时候,由于应用程序的数据大小、类型都是不可预估的,而TCP协议提供了缓冲机制来处理这些数据,如在数据量很小的时候,TCP会将数据存储在一个缓冲空间中,等到数据量足够大的时候在进行发送数据,这样子能提供传输的效率并且减少网络中的通信量,而且在数据发送出去的时候并不会立即删除数据,还是让数据保存在缓冲区中,因为发送出去的数据不一定能被接收方正确接收,它需要等待到接收方的确认再将数据删除。同样的,在接收方也需要有同样的缓冲机制,因为在网络中传输的数据报到达的时间是不一样的,而且TCP协议还需要把这些数据报组装成完整的数据,然后再递交到应用层中。
全双工通信
在TCP连接建立后,那么两个主机就是对等的,任何一个主机都可以向另一个主机发送数据,数据是双向流通的,所以TCP协议是一个全双工的协议,这种机制为TCP协议传输数据带来很大的方便,一般来说,TCP协议的确认是通过捎带的方式来实现,即接收方把确认信息放到反向传来的是数据报文中,不必单独为确认信息申请一个报文,捎带机制减少了网络中的通信流量。由于双方主机是对等的存在,那么任意一方都可以断开连接,此时这个方向上的数据流就断开了,但是另一个 方向上的数据仍是连通的状态,这种情况就称之为半双工。
流量控制
在前面讲过,一条TCP连接每一侧的主机都设置了缓冲区域。当该接收方收到数据后,它就将数据放入接收缓冲区,当确认这段数据是正常的时候,就会向发送方返回一个确认。并且向相关的应用层递交该数据,但不一定是数据刚一到达就立即递交。事实上,接收方应用也许正忙于其他任务,甚至要过很长时间后才会去处理这些数据。这样子如果接收方处理这些数据时相对缓慢,而发送方发送得太多、太快,就会很容易地使接收方的接收缓冲区发生溢出。
因此TCP提供了流量控制服务(flow-control service)以消除发送方使接收方缓冲区溢出的可能性。流量控制是一个速度匹配服务,即发送方的发送速率与接收方应用程序的读取速率相匹配,TCP通过让发送方维护一个称为接收窗口(receive window)的变量来提供流量控制,是的,你没看错,是接收窗口(rwnd),它用于给发送方一个指示:接收方还能接收多少数据,接收方会将此窗口值放在 TCP 报文的首部中的窗口字段,然后传递给发送方,这个窗口的大小是在发送数据的时候动态调整的。
那可能有人问,这个窗口既然是动态调整的,那有没有可能是0,这样子发送方不就是没法继续发送数据到接收方了?为了解决这个问题,TCP协议的规范中有些要求,当接收方主机的接收窗口为0时,发送方继续发送只有一个字节的报文段,这些报文段将被接收方接收,直到缓存清空,并在确认报文中包含一个非0的接收窗口值。
流量控制是双方通信之间的控制信息,这是很有必要的,比如两个性能不对等的主机,建立了TCP协议连接,但是其中一个主机一直发送数据,但是接收的主机来不及处理,这样子的处理就不是最佳的,因此,TCP协议中使用滑动窗口的流量控制方法,它允许接收方根据自身的处理能力来确定能接收数据的多少,因此会告诉发送方可以发送多少数据过来,即窗口的大小,而发送方尽可能将数据都多发到对方那里,所以发送方会根据这个窗口的大小发送对应的数据 ,通俗地来说就是接收方告诉发送方“我还有能力处理那么多的数据,你就发那么多数据给我就行了,不要发多了,否则我处理不了”。
差错控制
除了确认与重传之外,TCP协议也会采用校验和的方式来检验数据的有效性,主机在接收数据的时候,会将重复的报文丢弃,将乱序的报文重组,发现某段报文丢失了会请求发送方进行重发,因此在TCP往上层协议递交的数据是顺序的、无差错的完整数据。
拥塞控制
什么是拥塞?当数据从一个大的管道(如一个快速局域网)向一个较小的管道(如一个较慢的广域网)发送时便会发生拥塞。当多个输入流到达一个路由器,而路由器的输出流小于这些输入流的总和时也会发生拥塞,这种是网络状况的原因。
如果一个主机还是以很大的流量给另一个主机发送数据,但是其中间的路由器通道很小,无法承受这样大的数据流量的时候,就会导致拥塞的发生,这样子就导致了接收方无法在超时时间内完成接收(接收方此时完全有能力处理大量数据),而发送方又进行重传,这样子就导致了链路上的更加拥塞,延迟发送方必须实现一直自适应的机制,在网络中拥塞的情况下调整自身的发送速度,这种形式对发送方的控制被称为拥塞控制(congestion control),与前面我们说的流量控制是非常相似的,而且TCP协议采取的措施也非常相似,均是限制发送方的发送速度。
端口号
TCP协议的连接是包括上层应用间的连接,简单来说,TCP连接是两个不同主机的应用连接,而传输层与上层协议是通过端口号进行识别的,如IP协议中以IP地址作为识别一样,端口号的取值范围是0~65535,这些端口标识着上层应用的不同线程,一个主机内可能只有一个IP地址,但是可能有多个端口号,每个端口号表示不同的应用线程。一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等,这些服务完全可以通过1个IP地址来实现,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址只能识别一台主机而非主机提供的服务,这些服务就是主机上的应用线程,因此是通过 IP地址+端口号
来区分主机不同的线程。
三次握手
TCP 的三次握手,意思就是建立连接的时候客户端与服务器之间需要三次数据包的交流。
- 客户端发送给服务器一个请求连接数据包,即发送了一个指向服务器目标端口的一个
SYN 位为 1
的TCP 报文。 - 服务器接收到客户端的连接请求之后,会回应一个
SYN 位为 1 的TCP 报文,表示同意连接。并且,会把 ACK 位也置 1 表示确认收到上次消息
。 - 客户端接收到服务器的同意连接的数据包之后,还要回复一个
ACK 为 1 的 TCP 报文,表示确认收到
。
如下图所示,这就是 TCP 的三次握手。
数据传输
TCP 以段为单位发送数据
在建立 TCP 连接的同时,也可以确定发送数据包的单位,我们也可以称其为“最大消息长度”(MSS:Maximum Segment Size)。最理想的情况是,最大消息长度正好是 IP 中不会被分片处理的最大数据长度。
TCP 在传输大量数据时,是以 MSS 的大小将数据进行分割发送的。进行重发时也是以 MSS 为单位。
MSS 是在三次握手的时候,在两端主机之间被计算得出。两端的主机在发出建立连接的请求时,会在 TCP 首部中写入 MSS 选项,告诉对方自己的接口能够适应的 MSS 的大小(为附加 MSS 选项,TCP首部将不再是20字节,而是 4 字节的整数倍)。然后会在两者之间选择一个较小的值投入使用(在建立连接时,如果某一方的 MSS 选项被省略,可以选为 IP 包的长度不超过 576 字节的值(IP 首部 20 字节,TCP 首部 20 字节,MSS 536 字节))。
例如客户端发送一段 3500 长度的数据到服务端,假设确定的 MSS 长度为1000,数据传输的过程如下所示:
四次挥手
TCP 的四次挥手,意思就是释放连接的时候客户端与服务器之间需要四次数据包的交流。
- 客户端发送给服务器一个请求释放连接的数据包,即发送了一个指向服务器目标端口的一个 FIN 位为 1 的TCP 报文,表示客户端没有数据要发送了,但是仍然可以接收数据;并且 ACK 位也为 1,表示对上次传输数据结果的确认。并且之后处去等待状态,等待服务器的两次回应。
- 服务器接收到客户端的释放连接请求之后,会先回应一个 ACK 位为 1 的报文,表示确认收到。但是,这时服务器可能还有数据没有发送完成,继续发送数据。
- 服务器发送完数据之后,发送一个 FIN 为 1 的 TCP 报文,表示我也没有要发送的数据了,你可以释放连接了。当然 ACK 位仍然为 1 。
- 客户端接收到服务器的同意释放连接的数据包之后,回复一个 ACK 为 1 的 TCP 报文,表示确认收到。
如下图所示,这就是 TCP 的四次挥手。
抓包
利用 wireshark 工具抓包分析可以很清晰的看到这两个流程。
- 打开 wireshark 软件 开启抓包,过滤规则设置为 tcp 。
- 开发板连接上网络后,运行 samples 里面的 tcpclient 例程,
这样就可以很方便的看到 TCP 的三次握手与四次挥手的全过程,如下图所示:
参考
- 正点原子《开拓者之FPGA开发指南V3.2》
- 野火 LWIP
- RT-Thread 网络教程