【Linux笔记】网络部分——传输层协议TCP(1)

40.传输层协议TCP(1)

文章目录

简单概述

TCP协议是传输层协议,与UDP同属传输层,位于操作系统中。TCP具有发送缓冲区和接收缓冲区,上层应用程序通过writesend等函数将数据序列化后拷贝到TCP发送缓冲区,数据发送的节奏完全由操作系统控制。TCP被称为传输控制协议,因为它能自主决定数据发送的时机、数量和错误处理,而UDP无法做到这一点。TCP在发送数据时需要添加报头信息,其协议设计较为复杂以保证可靠性。

TCP报头中体现了多种可靠性机制,如确认应答、超时重传、连接管理等。这些机制解决了服务器重启时绑定失败等问题。TCP还具备滑动窗口、流量控制、拥塞控制等功能,这些特性使其成为面向连接的可靠传输协议。

TCP协议段格式

TCP报头标准长度为20个字节,与UDP报头类似TCP报头仍然为固定长度。这是在不包括选项字段的前提下才能说TCP报头是固定长的,但选项仍然是报头的一部分

  • TCP报文需要包含16位源端口号和16位目的端口号,以确保数据能正确交付给上层应用。

  • TCP还包含32位序号和确认序号,用于保证报文可靠传输、确认应答和按序到达功能,这个字段与后面的确认应答机制有很大的关联。

  • TCP报头中四位首部长度字段表示标准报头加选项的总长度,但它是有"单位"的,其基本计算单位为四字节,实际报头长度为该字段值乘以四。标准报头长度为20字节时该字段值为5,但是TCP报头还包含长度不固定的选项字段,所以TCP报头长度范围为20到60字节。

    TCP报头中有四维的首部长度但没有描述数据长度的字段,这是因为TCP是面向字节流的协议,不对报文内容做解释,接收到的数据直接放入接收缓冲区形成流式结构。UDP是面向数据报的协议,需要保证获取完整报文,因此UDP报头中包含长度字段。

  • TCP报头还包含6个标志位

    • URG表示紧急指针是否有效
    • ACK表示确认号是否有效,也就是说这个报文的应答序号是否有效,这个标志位与TCP的确认应答机制有关
    • PSH表示提⽰接收端应⽤程序⽴刻从TCP缓冲区把数据读⾛
    • RST表示复位,表示对方要求重新建立连接
    • SYN表示请求建立连接,我们把携带SYN标识的称为同步报⽂段 ,FIN表示通知对⽅, 本端要关闭了, 我们称携带FIN标识的为结束报⽂段。这两个标志位与TCP建立连接的三次握手和断开连接时的四次挥手有关
  • 16位窗口大小表示发送方的接收缓冲区还能接收数据的大小,与TCP的滑动窗口机制有关

  • 16位检验和表示发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含TCP⾸部, 也包含TCP数据部分.

  • 16位紧急指针标识数据段的哪部分数据是紧急数据

解包和分用

  1. 解包:TCP解包时先读取前20字节标准报头,从中提取四位首部长度字段,乘以四得到真实报头长度,从而分离报头和有效载荷。

  2. 分用:分用则通过目的端口号实现,将有效载荷交付给上层对应应用。

源码

cpp 复制代码
struct tcphdr {
	__u16	source;
	__u16	dest;
	__u32	seq;
	__u32	ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u16	res1:4,
		doff:4,
		fin:1,
		syn:1,
		rst:1,
		psh:1,
		ack:1,
		urg:1,
		ece:1,
		cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
	__u16	doff:4,
		res1:4,
		cwr:1,
		ece:1,
		urg:1,
		ack:1,
		psh:1,
		rst:1,
		syn:1,
		fin:1;
#else
#error	"Adjust your <asm/byteorder.h> defines"
#endif	
	__u16	window;
	__u16	check;
	__u16	urg_ptr;
};

确认应答机制

在TCP通信中,客户端发送消息给服务器后,服务器需要给客户端发送应答,以确保客户端确认消息已被服务器接收,这种策略称为确认应答。

机制细节:

  • 应答的发送:当客户端向服务器发送数据时,服务器必须对接收到的数据进行确认。这种确认不是简单的发送一个随意的消息,而是需要发送一个完整的TCP报头。
  • 报头中的序号和确认序号

报头中的序号和确认序号

  • 发送和接收缓冲区可以想象成字符数组,每个元素是一个字节,数据放入数组后每个字节都有编号,这个编号就是报文中序号的来源。TCP将每个字节的数据都进⾏了编号. 即为序列号

  • 为了区分不同的报文和对应的确认应答,每个报文都需要被编号(32位序号)。当服务器收到一个报文时,会将该报文的编号加一作为确认应答的编号(32位确认序号)。例如,收到编号为10的报文,服务器会发送编号为11的确认应答。客户端在收到确认应答后,可以确认该编号之前的所有数据都已被服务器接收(关键:该确认序号之前的所有数据都已被接收)。

  • 这种机制不仅用于确认数据的接收情况,还用于判断数据的连续性。通过确认序号,客户端可以准确知道哪些数据已被成功接收,哪些数据需要重新发送。这种机制在复杂的网络环境中尤为重要,能够有效应对网络延迟和丢包等问题,确保数据的完整性和可靠性。

  • 报文在网络传输过程中可能因网络情况导致到达顺序与发送顺序不一致,因此需要通过序号来保证报文可按序到达。

可靠性保证:

  • 服务器发送应答时,本质上也是在发送信息,但服务器不需要关心应答是否被客户端收到,因为如果客户端没有收到应答,会再次询问。
  • 可靠性关注的是历史报文的可靠性,而非最新消息的可靠性。TCP是全双工的,因此从左到右和从右到左的通信都能保证可靠性。
  • 确认应答机制的核心在于对旧消息的应答,确保消息被对方收到。服务器发送消息后,客户端必须应答,即使没有数据要发送。双方操作系统会自动进行确认工作,无需对确认再做确认。

效率方面:

  • 为了提高通信效率,TCP采用了批量发送和批量确认的机制。客户端可以一次性发送多个报文,服务器在收到这些报文后统一进行确认应答。这种方式可以在时间上重叠发送和确认的过程,从而减少等待时间,提高整体通信效率。
  • 在批量发送过程中,每个报文都需要被单独编号,以便服务器能够准确识别和确认。服务器在收到多个报文后,会对每个报文进行单独的确认应答,确保每个报文的可靠性。
  • 客户端在收到确认应答后,可以根据确认序号判断哪些数据已被成功接收,从而决定下一次发送数据的起始点
  • 捎带应答机制

捎带应答机制

在通信过程中,客户端发送报文后,服务端会进行应答,应答报文通常只包含TCP报头而不带数据。应答报文中会包含确认序号,用于确认已收到的报文序号。TCP协议中同时存在序号和确认序号两个独立字段,是因为在实际通信中,服务端在应答时可能同时携带数据,此时报文既是应答又是数据,因此需要同时使用序号和确认序号。这种机制称为捎带应答机制,它允许在确认应答的同时传输数据,提高了通信效率。捎带应答机制使得TCP通信更接近人类日常交流模式,既有确认又有数据交换。在TCP通信中,客户端和服务端都需要进行这种双向的确认和数据交换,因此序号和确认序号必须同时存在。序号用于标识数据的编号,确认序号用于确认已收到的数据,两者共同保证了TCP通信的可靠性和有序性。

流量控制

当服务器来不及接收时,报文会被操作系统丢弃且不发送应答,客户端检测到无应答后会重传。这种做法虽然可行但会浪费网络资源,因为报文已经经过网络传输到达目标主机却被丢弃。TCP的流量控制机制可以解决这个问题,通过让客户端动态调整发送数据量来匹配服务器的接收能力。流量控制是TCP传输控制的重要环节,当服务器来不及接收时客户端可以减慢或停止发送。要实现流量控制需要客户端了解服务器的承载能力,就像用量杯倒水时需要知道当前水位来调整倒水速度和量的大小。

16位窗口大小

TCP报头中包含16位窗口大小字段,用于流量控制。TCP流量控制机制通过窗口大小字段让客户端了解服务器的接收能力,动态调整发送数据量。当服务器接收能力下降时,可以通过减小窗口大小通知客户端减慢发送速度,当服务器接收缓冲区空间很大时,可以通过窗口大小字段告知客户端可以增加发送量。这种机制避免了网络资源浪费,因为客户端不会发送超出服务器处理能力的数据。窗口大小就像量杯上的刻度,客户端根据这个"刻度"来控制数据发送量,避免发送过多服务器无法处理的数据。

  • 通信双方都需要进行流量控制,每次发送报文时都要在16位窗口大小字段中填写自己的接收缓冲区剩余空间大小,通告给对方。
  • TCP缓冲区在内核中大小通常是固定的,接收缓冲区最大为2的16次方,可以通过修改内核编译选项或窗口扩大因子调整窗口大小。

超时重传

TCP通信中丢包问题分为两种情况:数据丢失和应答丢失。当主机A向主机B发送消息时,如果数据丢失,主机B从未收到报文,因此不会发送应答;如果应答丢失,主机B已收到数据但应答未到达主机A。主机A无法区分这两种情况,只能通过设定超时时间间隔来判断是否需要重传。超时重传机制要求主机A在发送数据时设定一个时间间隔,若在该时间内未收到应答,则判定数据丢失并进行重传。

  • 序号机制不仅用于确认报文的正确顺序,还能帮助主机B识别并丢弃重复报文。
  • 超时时间间隔的设定需要考虑网络状况的变化,不能采用固定值,必须根据实际情况动态调整。时间太短可能导致正常传输中的报文被误判为丢失,增加网络负担;时间太长则会降低发送效率。
    • Linux中(BSD Unix和Windows也是如此), 超时以500ms为⼀个单位进⾏控制, 每次判定超时重发的超时时间都是500ms的整数倍.
    • 如果重发⼀次之后, 仍然得不到应答, 等待 2*500ms 后再进⾏重传.
    • 如果仍然得不到应答, 等待 4*500ms 进⾏重传. 依次类推, 以指数形式递增.
    • 累计到⼀定的重传次数, TCP认为⽹络或者对端主机出现异常, 强制关闭连接.

连接管理机制

TCP的链接管理机制,涉及三次握手和四次挥手。在讲解这些机制之前,需要先介绍TCP报头中的三个标志位。标志位的存在是为了区分不同类型的TCP报文,因为服务器端可能同时收到来自多个客户端的请求,包括建立连接、数据传输和断开连接等不同操作。服务器需要根据报文类型采取不同的处理策略,例如建立稳定连接、流量控制或释放资源。

ACK标志位

ACK标志位用于表明当前报文是对历史报文的确认,即它是一个确认报文。设置ACK标志位并不代表报文正文部分不包含数据,而是表示该报文具备确认功能。接收方在收到ACK标志位为1的报文时,应关注确认序号字段。

SYN和FIN标志位

  • SYN标志位用于TCP连接的建立过程,具体体现在三次握手中。当客户端希望与服务器建立连接时,会发送一个SYN标志位置1的报文,表示请求建立连接。服务器收到后,会回复一个SYN+ACK报文,表示同意建立连接。最后,客户端再发送一个ACK报文确认,完成三次握手。

  • FIN标志位用于断开连接,表示通信结束时需要终止链接。

三次握手和四次挥手的过程

服务端状态变化:

  • CLOSE-\>LISTEN\]服务器调用`listen`进入LISTEN状态,等待客户端连接

  • SYN_RCVD -\> ESTABLISHED\] 服务端⼀旦收到客⼾端的确认报⽂, 就进⼊ESTABLISHED状态, 可以进⾏读写数据了

  • CLOSE_WAIT -\> LAST_ACK\] 进⼊CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调⽤close关闭连接时, 会向客⼾端发送FIN, 此时服务器进⼊LAST_ACK状态, 等待最后⼀个ACK到来(这个ACK是客⼾端确认收到了FIN)

客户端状态变化:

  • CLOSED -\> SYN_SENT\] 客⼾端调⽤connect, 发送同步报⽂段;

  • ESTABLISHED -\> FIN_WAIT_1\] 客⼾端主动调⽤close时, 向服务器发送结束报⽂段, 同时进⼊FIN_WAIT_1;

  • FIN_WAIT_2 -\> TIME_WAIT\] 客⼾端收到服务器发来的结束报⽂段, 进⼊TIME_WAIT, 并发出LAST_ACK;

TCP状态转换汇总

理解

  1. 在TCP三次握手过程中,客户端和服务器对链接建立的时间认知存在差异。客户端通常在发送最后一个ACK后即认为链接已建立,而服务器需要等待收到该ACK才能确认链接建立,这导致服务器建立链接的时间比客户端稍晚。
  2. ACK在网络传输中需要一定时间,虽然最后一个ACK不需要确认,但它确实被发送给对方。如果最后一个ACK丢失,客户端认为链接已建立,而服务器认为未建立,这将导致客户端尝试发送数据时,服务器因三次握手未完成而拒绝接收,并可能返回重置标志位RST。
  3. TCP是全双工的,因此断开链接需要双方各自确认。例如,客户端可能希望断开链接,但服务器仍希望继续发送数据,此时需要协商。四次挥手必须完成,因为TCP的全双工特性要求双方在双向通信中都确认断开。在某些情况下,四次挥手可能简化为三次,如果双方同时希望断开链接,FIN和ACK报文可以合并发送。但大多数情况下,由于双方断开链接的意愿不一定同步,四次挥手更为常见。
  4. 三次握手的本质可以理解为四次握手的简化,服务器在建立链接时将ACK和建立链接的请求合并为一个报文,从而实现捎带应答。这种合并使得四次握手简化为三次握手。三次握手确保了双方在建立链接时的双向可靠性,即从左向右和从右向左的通信均被确认。建立链接需要双方同意,因为TCP是全双工的,必须在两个方向上建立链接。断开链接时的四次挥手与建立链接时的三次握手形成对比,因为断开链接时双方的意愿不一定同步,难以合并报文。但在某些情况下,如果双方同时希望断开链接,四次挥手也可能简化为三次。
相关推荐
二进制coder7 小时前
Linux I2C子系统全面详解:从理论到实战
linux·运维·服务器
菲橙7 小时前
5.2 MCP服务器
运维·服务器
lang201509287 小时前
WebSocket子协议STOMP
网络·websocket·网络协议
Lester_11017 小时前
嵌入式学习笔记 - 用泰勒公式解决 tanh函数
笔记·学习·算法
饺子大魔王的男人7 小时前
3秒传输GB级文件:FastSend让P2P共享告别云存储依赖
网络·网络协议·p2p
sunshine~~~7 小时前
【笔记】macOs arm架构安装虚拟机Ubuntu环境:ROS2 + Python开发
arm开发·笔记·python·macos·ros2
帅帅梓7 小时前
Jenkins
运维·jenkins
在坚持一下我可没意见7 小时前
Java 网络编程:TCP 与 UDP 的「通信江湖」(基于TCP回显服务器)
java·服务器·开发语言·笔记·tcp/ip·udp·java-ee
弈风千秋万古愁8 小时前
【PID】连续PID和数字PID chapter1(补充) 学习笔记
笔记·学习·算法·matlab