传输层(Transport Layer)的主要任务就是负责向两台主机进程之间的通信提供通用的 数据传输服务。应用进程利用该服务传送应用层报文。
网络协议族中有两个具有代表性的传输层协议,分别是 TCP 和 UDP。
传输控制协议(Transmission Control Protocol,简称 TCP)是一种 面向连接(连接导向)的、可靠的、 基于 IP 协议的传输层协议。
参考:数据包结构
TCP 首部标志比特有 6 个:URG、ACK、PSH、RST、SYN、FIN
控制位 | 名称 | 说明 |
---|---|---|
URG | Urgent Flag | 紧急指针 |
ACK | Acknowledge Flag | 确认序号有效 |
PSH | Push Flag | 尽可能快地将数据送往接收进程 |
RST | Reset Flag | 可能需要重现创建建 TCP 连接 |
SYN | Synchronize | 同步序号来发起一个连接 |
FIN | Finish | 发送方完成发送任务,要求释放连接 |
Seq | Sequance number | 序列号 |
TCP 提供 面向连接 的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作,也就是说无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。
三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。
三次握手的目的:
在 Socket 编程中,这一过程由客户端执行 connect
来触发。
SYN_SENT
状态,等待服务端确认。SYN_RCVD
状态。握手过程中传送的包里不包含数据,只有三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP 连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
SYN 报文
SYN/ACK 报文
ACK 报文
未连接队列
在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的 SYN 包(syn=j)开设一个条目,该条目表明服务器已收到 SYN 包,并向客户端发出确认,正在等待客户端的确认包。这些条目所标识的连接在服务器处于 SYN_RECV
状态,当服务器收到客户端的确认包时,删除该条目,服务器进入 ESTABLISHED
状态。
为什么建立 TCP 连接需要三次握手?
主要是为了防止服务端开启无用的连接。
因为我们知道网络传输是有延时的,因为终端间隔了非常远的距离,数据包通过光纤以及各种中间代理服务器进行传输,但是在服务端和客户端的传输过程中,往往由于网络传输的不稳定原因丢失了数据包,客户端一直没有收到服务端返回的数据包,客户端可能设置了超时时间关闭了连接创建,那么就会再发起新的请求。如果没有第三次握手,服务端是不知道客户端到底有没有接收到服务端返回给他的数据的,客户端也没有一个确认说要关闭还是要创建这个请求,服务端的端口就一直开着,等着客户端发送实际的请求数据,那么这个时候开销就浪费了,服务端不知道这个连接已经创建失败了,可能客户端已经创建别的连接去了。
所以我们需要三次握手来确认这个过程,让服务端和客户端能及时察觉到网络原因导致的网络连接的关闭的问题,从而规避网络传输中因为延时导致导致的服务器开销问题。
三次握手中的第一次握手可以携带数据吗?
不可以,因为三次握手还没完成。
第三次握手可以发送数据吗?为何?
可以。因为能够发出第三次握手报文的主机,肯定接收到第二次(来自服务端)的握手报文。因为伪造 IP 的主机不会收到第二次报文。
对方难道不可以将数据缓存下来,等握手成功后再提交给应用程序?
这样会放大 SYN FLOOD 攻击。如果攻击者伪造了成千上万的握手报文,携带了 1K+ 字节的数据,而接收方会开辟大量的缓存来容纳这些巨大数据,内存会很容易耗尽,从而拒绝服务。
四次挥手即终止 TCP 连接,就是指断开一个 TCP 连接时,需要客户端和服务端总共发送 4 个包以确认连接的断开。在 Socket 编程中,这一过程由客户端或服务端任一方执行 close
来触发。
由于 TCP 连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个 FIN
来终止这一方向的连接,收到一个 FIN
只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个 TCP 连接上仍然能够发送数据,直到这一方向也发送了 FIN
。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。
四次挥手流程图
FIN
报文段,用于关闭客户端到服务器端的数据传送,客户端进入 FIN_WAIT_1
状态。意思是说「我客户端没有数据要发给你了」,但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。FIN
报文段,回复 ACK
报文段,Acknowledgment Number 为 Sequence Number 加 1,告诉客户端,你的请求我收到了,我同意你的关闭请求。这个时候客户端就进入 FIN_WAIT_2
状态。FIN
报文段,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入 LAST_ACK
状态。FIN
报文段后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送 ACK
报文段回复服务端,然后进入 TIME_WAIT
状态,如果服务端端没有收到 ACK
则可以重传。服务器端收到 ACK
后,就知道可以断开连接了。客户端等待了 2MSL(通常是两分钟)后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。MSL(Maximum Segment Lifetime)报文最大生存时间。维持 2MSL 时长的 TIME-WAIT 状态,保证至少一次报文的往返时间内端口是不可复用。
所以如果服务端的数据全部发送完,是没有第三次挥手,直接进入第四次挥手。
为什么断开 TCP 连接需要四次挥手?
由于 TCP 连接采取全双工的通信方式,因此每个方向都必须单独进行关闭,这个原则是当一方完成它的数据发送任务后就能发送一个 FIN
来终止这个方向的连接。收到一个 FIN
只意味着这一方向上没有数据流动,一个 TCP 连接在收到一个 FIN
后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
为什么基于 TCP 的程序往往都有个应用层的心跳检测机制?
TCP 建立链接后,只是在两端的内核里面维持 TCP 信息,实际上并没有一个物理的连接通路,对端这个时候挂了,谁也不知道。
用户数据报协议(User Datagram Protocol,UDP),又称使用者资料包协定,是一个简单的面向数据包的传输层协议,正式规范为 RFC 768。
在 TCP/IP 模型中,UDP 为网络层以上和应用层以下提供了一个简单的接口。UDP 只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以 UDP 有时候也被认为是不可靠的数据报协议)。UDP 在 IP 数据报的头部仅仅加入了复用和数据校验(字段)。
基于 UDP 协议的有:
TCP | UDP | |
---|---|---|
连接性 | 面向连接 | 无连接 |
双工性 | 全双工(1:1) | n:m |
可靠性 | 可靠(重传机制) | 不可靠(丢包后数据丢失) |
有序性 | 有序(通过 SYN 排序) | 无序 |
有界性 | 无,有沾包情况 | 有消息边界,无沾包 |
拥塞控制 | 有 | 无 |
传输速度 | 慢 | 快 |
量级 | 低 | 20-60 字节 |
头部大小 | 高 | 8 字节 |