传输层
传输层是OSI模型的第四层,主要负责端到端的数据传输,确保数据可靠、有> 序地从源设备传送到目标设备。其主要功能包括:
- 端到端通信:在源和目标设备之间建立连接,确保数据准确传输。
- 数据分段与重组:将数据分割成适合网络传输的较小单元(如TCP段或UDP数据报),并在接收端重新组合。
- 错误检测与恢复:通过校验和、确认机制等检测并纠正传输中的错误。
- 流量控制:调节数据传输速率,防止接收方过载。
- 拥塞控制:在网络拥堵时减少数据发送量,避免进一步恶化。
OSI模型
常见的传输层协议
UDP
UDP是工作在OSI(开放系统互连,Open Systems Interconnection)模型中传输层的协议。它使用IP作为底层协议,是为应用程序提供一种以最少的协议机制向其他程序发送消息的协议。其主要特点是无连接,不保证可靠传输和面向报文。RFC 768为IETF(互联网工程部,Internet Engineering Task Force)
提供的UDP标准。
UDP协议的特点:
- 无连接:知道对端的IP和端口号就直接发送报文,不管是否建立连接
- 不可靠:没有确认机制,如果因为网络的问题没有发送到对端,不会给应用层返回任何的错误信息。
- 面相数据报:应用层不能够灵活的控制读写的次数。
UDP报头

- 16为长度表示报头+数据的总长度,因此UDP一次能够发送的最大长度是2^16字节,也就是64KB左右,因此,UDP更适合较短的数据的发送。
- 16校验位,如果校验出错,报文会被直接丢弃。
UDP接口

在绑定套接字的时候,使用SOCK_DGRAM
TCP(重点,难点)
TCP(传输控制协议,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。它位于OSI模型的第四层(传输层),主要用于在不可靠的互联网上提供可靠的数据传输服务。
TCP的主要特点:
- 面向连接:通信前需要通过"三次握手"建立连接,通信结束后通过"四次挥手"断开连
- 可靠性:通过确认机制、重传机制、校验和等方式确保数据准确无误地传输。
- 有序性:数据按发送顺序到达接收端,TCP会对数据包进行排序。
- 流量控制:通过滑动窗口机制防止发送方发送数据过快导致接收方无法处理。
- 拥塞控制:通过慢启动、拥塞避免等算法防止网络过载。
下面我们会分别谈论TCP这些特点
TCP连接管理机制
TCP协议本身就是面相连接的,意味着在进行通信的时候,首先要将两台主机进行连接,这里就引入我们下面将要谈论的三次握手 和四次挥手 。
三次握手 :
假设我们的客户端请求访问服务器的例子。服务器将特定的端口绑定后就会进入监听(Listen状态),等待我们的客户端进行访问。我们客户端调用connect函数,之后服务器就会收到SYN=1的请求报头,表示请求连接,我们客户端的accept函数,会返回ACK=1表示确认连接的返回报头,但是TCP是全双工 ,因此,我们的服务器也要发送SYN=1的请求连接的请求报头,这个时候,为了简化,最终服务器会发送ACK=1&&SYN=1的报头,客户端接收到了之后,返回ACK=1的报头,表示针对服务器建立连接的请求表示同意。
当完成这些之后,我们客户端就可以通过套接字进行通信了。
图解 :
四次挥手 :
假设我们的客户端请求断开连接,这个时候,我们的客户端会处于FIN_WAIT_I的状态,发送FIN=1的报头给服务器,表示请求断开连接,服务器现在肯定阻塞等待哪里,因为我们服务器正在等待客户端发送的信息,这个时候,服务器会结束read,直接返回0,表示客户端断开,这个时候,我们单方面解开了和客户端的连接(表示客户端不能发送信息给我们了,但是我们还能发送信息给客户端)。这个时候,我们在编写服务器代码的时候,进行判断,if(read() = 0) close(sockfd),关闭通信套接字,这个时候我们发送FIN=1的报头给客户端,客户端接收到了之后,返回ACK=1,这个时候四次挥手完全结束,客户端和服务器的连接完全断开了。
图解 :
TCP的确认应答和超时重传
TCP面向连接,保证了可靠性,因此,每个发送的报文,对端都会返回ACK=1的报文,表示接收到了你的报文。
在上面提到的三次握手 和四次握手 中我们以及能够或多或少的感觉到这一点了。
因此,当我们发送方发送报文后长时间没有接收到ACK的时候,发送方就会进行超时重传。
TCP TIME_WAIT状态引起的绑定失败
通过上面四次挥手的图中,可以看到,对端发送FIN的时候,最开始的请求方会进入FIN_WAIT的状态,这种状态下,我们的请求方会在一定时间内拒绝你对当前的端口的bind,他的目的是防止历史遗留在路由器上的报文,在干扰下一次的连接。
但是在一些场景下这种机制是不合理的 :
比如一些不活跃的用户,我们的的服务器应该对其进行清理,这种时候我们会出现大量TIME_WAIT状态的链接,在高并发的情况下,这种会影响服务器的性能。
解决方法 :
利用setsockopt接口,通过SO_REUSEADDR套接字选项,通知内核,某个Listen状态的套接字可以重复使用。
TCP报头
下面我会根据TCP报头的这些字段详细解释其他TCP处理的机制
TCP滑动窗口机制(确认序号 + 序号)
TCP滑动窗口机制又这里的32确认序号和32为序号保证,我们知道TCP会进行确认应答,但是如何更加合理和高效的确认应答就是利用这里的32为序号和确认序号。
确认序号的机制 :表示当前数字之前的报文都已经接收到了,比如确认序号为1001返回给发送端,发送端就知道0~1000都已经接受到了。
滑动窗口 :和算法中的滑动窗口的思想一样我们每次发送方将窗口中的内容依次发送给对端,对端接收到了之后返回确认序号,如果确认序号为2001,我们的窗口的左侧就可以直接移动到2001,窗口的右侧也可以移动到6001,表示5001~6001也可以发送。
我们通过确认序号的机制,就可以确认报文是否发送成功了。
URG | ACK | PSH | RST | SYN | FIN
- URG : 紧急指针是否有效。
TCP紧急指针是TCP协议中的一个重要概念,用于处理紧急数据。紧急数据是一种需要优先处理的数据,通常用于发送一些重要的通知或控制信息。
紧急指针的作用 :当TCP报文的URG标志位被置为1时,TCP头部的紧急指针字段会记录一个偏移量,指向紧急数据的最后一个字节的下一个字节
这意味着紧急指针只能标识一个字节的数据。紧急数据的字节号可以通过以下公式计算:
- ACK : 确认应答
- PSH : 提醒我们的应用层应该及时将缓冲区中发送的报文取走。
- RST : 请求重新建立连接。
- SYN : 请求建立连接。
- FIN : 请求断开连接。
紧急指针,上面已经介绍
16位窗口大小
我们知道TCP中的缓冲区是定长的,因此,16窗口大小表示当前缓冲区的剩余量。
16位校验
保证数据的可靠性,如果校验失败,报文会被直接丢弃。
4位首部长度
由于我们的报头有参数字段,因此报头的具体长度是不确定的,这个时候四位首部长度表示[0,15]这个大小,但是我们首部长度的基本单位是四字节,所以四位首部长度可以表示[0,60]这个大小,如果参数为空,报头的长度为20字节,因此,四位首部长度具体能够表示[20,60]。
源端口和目的端口
这个不用解释
流量控制
TCP的缓冲区是定长的,因此,如果用户突然发送大量的数据可能直接导致缓冲区溢出的问题,因此TCP还要处理拥塞的问题。
解决方式 :这个问题的解决方式解释我们报头中的窗口大小,发送端在收到确认应答的时候同时会受到接收端目前窗口 的信息,这个时候,通过调整发送方滑动窗口的大小就可以减少发送量,是不是逻辑的闭环。
拥塞控制
虽然 TCP 有了滑动窗口这个大杀器, 能够高效可靠的发送大量的数据. 但是如果在刚开
始阶段就发送大量的数据, 仍然可能引发问题.
因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络
状态下, 贸然发送大量的数据, 是很有可能引起雪上加霜的.
TCP 引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按
照多大的速度传输数据。
当 TCP 开始启动的时候, 慢启动阈值等于窗口最大值。
- 在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回 1;
少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞; - 当 TCP 通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
拥塞控制, 归根结底是 TCP 协议想尽可能快的把数据传输给对方, 但是又要避免给网络
造成太大压力的折中方案
慢启动的本质就是对当前网络环境进行探测
这样的好处就是最后发送的大小取决于网络的能力 && 接收方窗口的大小
面相字节流
建一个 TCP 的 socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区:
- 调用 write 时, 数据会先写入发送缓冲区中;
- 如果发送的字节数太长, 会被拆分成多个 TCP 的数据包发出;
- 如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或
者其他合适的时机发送出去; - 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;
- 然后应用程序可以调用 read 从接收缓冲区拿数据;
- 另一方面, TCP 的一个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这一
个连接, 既可以读数据, 也可以写数据. 这个概念叫做 全双工
这个时候就会出现经典的粘包问题 ,TCP区别于UDP他是面相字节流,UDP是面相数据报,面相字节流其实和文件操作很像,我们需要通过我们事先商量好的协议进行读取,比如HTTP协议或者我们程序员自己制定的协议,我们需要通过各种高级语言(c/c++,Java,Python...)通过字符串操作,让他的报文能够完整的显现出来。
面相字节流只是TCP的特性,并不是缺点,就和虽然UDP能够减少字符串处理,但是UDP发送的内容有限。这都是协议的特性,并不能说是缺点。
总结
传输层协议(TCP/UDP)是网络通信中的核心,负责在源主机和目标主机之间提供可靠或高效的数据传输服务。TCP(传输控制协议)通过三次握手建立连接,确保数据的可靠性和顺序,适用于对数据完整性要求高的场景,如网页浏览和文件传输。UDP(用户数据报协议)则提供无连接服务,传输速度快但不可靠,适合实时性要求高的应用,如视频流和在线游戏。两者各有优劣,选择时需根据具体需求权衡可靠性与效率。