文章目录
- 端口号
- UDP协议
- UDP报文
- UDP发送数据报过程
- TCP协议
- TCP报文
- 确认应答
- 超时重传
- 连接管理
- 流量控制
- 拥塞控制
- 补充
- 小结
- UDP实现TCP
- 总结
- TCP和UDP的区别
端口号
端口号(Port)标识了一个主机上进行通信的不同的应用程序。
简单的说,就是两台主机通信时,要想准确无误的在庞大复杂的互联网里找到目的主机就需要ip地址,而在目的主机众多应用程序里找到对应通信的程序则需要端口号。
- 常见的端口号
FTP 21
SSH 22
telnet 23
http 80
https 443
UDP协议
- 特性:不可靠,无连接,面向数据报
UDP报文
包长度:就是数据报最大长度。即最大长度为216B=64KB
ps:这里的64KB大小是整个udp报文的最大大小,是包括报头长度8B。
在Linux源码里大概如下
struct udp_header
{
uint16_t src_port;
uint16_t dst_port;
uint16_t udp_len;
uint16_t check;
};
UDP发送数据报过程
通信双发只要知道对方的ip和端口,就可以使用udp协议发送udp报文数据,对方的接受缓冲区就会接受,若是对方的接收缓冲区数据满了,再次发送数据包,就会产生丢包现象。
基于UDP应用层的协议:NFS,DNS,DHCP
TCP协议
- 特性:可靠,有连接,面向数据流
TCP报文
- 序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
- 确认应答号:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
- 首部长度:单位4B,表示TCP首部长度的大小。4位表示范围是[0, 15],所以TCP首部最大为60B,即选项长度最大为40B
- 标志位:区别不同类型的报文
ACK:确认应答报文(可能会携带数据 --> 捎带应答)
SYN:连接请求报文
FIN:断开连接报文
RST:重连请求报文
PSH:催促请求报文,用于提示接收端立刻取走接收缓冲区里的内容
URG:紧急报文,通过特殊方式传送紧急数据
确认应答
无论成功与否,一段都能准确清楚地知道所发数据报的状态
如图:
第一次握手:C端发SYN和序列号seq = x
第二次握手:S端发ACK+SYN和确认应答号ack=x+1,序列号seq = y。表示S端收到x前的消息,并发生自己的消息序列seq
第三次握手:C端收到消息,建立连接,发送ACK,确认应答号ack=y+1,序列号seq=x+1。表示C端收到y消息前的所有消息,seq=x+1表示消息从x+1开始。
每一次握手都可以清楚确认数据报的状态。
超时重传
无论哪一方在通信时,所处的某一段时间内未收到对方的应答消息,就会进行数据重发。
TCP协议会根据网络状态制定超时重传时间。时间太短,导致重传大,时间太长,重传效率低。
连接管理
三次握手和四次挥手的全过程
客户端
状态 | 过程 |
---|---|
[CLOSED -> SYN_SENT] | 客户端调用connect, 发送同步报文段; |
[SYN_SENT -> ESTABLISHED] | connect调用成功, 则进入ESTABLISHED状态, 开始读写数据; |
[ESTABLISHED -> FIN_WAIT_1] | 客户端主动调用close时, 向服务器发送结束报文段, 同时进入FIN_WAIT_1; |
[FIN_WAIT_1 -> FIN_WAIT_2] | 客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服务器的结束报文段; |
[FIN_WAIT_2 -> TIME_WAIT] | 客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出LAST_ACK; |
[TIME_WAIT -> CLOSED] | 客户端要等待一个2MSL(Max Segment Life, 报文最大生存时间)的时间, 才会进入CLOSED状态 |
- 为什么要等2MSL:为了让网络中传输的数据全部消失,从而不影响下次端口的数据传输
服务端
状态 | 过程 |
---|---|
[CLOSED -> LISTEN] | 服务器端调用listen后进入LISTEN状态, 等待客户端连接 |
[LISTEN -> SYN_RCVD] | 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端发送SYN确认报文 |
[SYN_RCVD -> ESTABLISHED] | 服务端一旦收到客户端的确认报文, 就进入ESTABLISHED状态, 可以进行读写数据了 |
[ESTABLISHED -> CLOSE_WAIT] | 当客户端主动关闭连接(调用close), 服务器会收到结束报文段, 服务器返回确认报文段并进入CLOSE_WAIT |
[CLOSE_WAIT -> LAST_ACK] | 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN) |
[LAST_ACK -> CLOSED] | 服务器收到了对FIN的ACK, 彻底关闭连接 |
- CLOSE_WAIT:如果没有调用close()关闭连接,则服务端会长时间保持这种状态,造成文件描述符fd泄露
流量控制
TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制
过程:在建立三次握手的过程中,双方可以获得TCP报文里相应对方的接收缓存区大小,此时就可以进行流量控制了。期间若S端接收缓冲区满了,C端就会定时进行窗口检测(会发PSH类型报文催促),S端有空间了也会主动向C端发送窗口更新通知(以报文形式)
- 滑动窗口
- 滑动窗口大小 = min(拥塞窗口,对方缓冲区大小)
- 滑动窗口在代码里的实现就是两个下标指针——[win_start, win_end]
win_start = seq
win_end = seq + min(滑动窗口大小, 拥塞窗口大小)- 滑动窗口只能向右移动
- 数据丢包,窗口不动,等待重传
- 应答(ACK)丢了,无关紧要。(收到三次及以上相同的ACK报文,就会进行快重传)
拥塞控制
从始至终,TCP协议都在对网络状况进行探测,这一过程就是拥塞控制
慢启动后,拥塞窗口开始以指数速率增长,达到阈值(ssthresh)后进行拥塞避免,线性速率增长,一旦发生网络拥塞,阈值 = 拥塞窗口 / 2,拥塞窗口 = 1,往复执行。
补充
- 捎带应答:为了提高TCP的工作效率,有时会将两个可以合并的报文合二为一。三次握手中的第二次握手就是使用了捎带应答,(SYN+ACK报文,两个报文合二为一)。
- 延迟应答:为了提高TCP的传输效率,接收端在收到应答后会等待一段时间或每几个包在应答一次(200ms或2个包)。
小结
做法 | |
---|---|
可靠性 | 校验和,序列号,确认应答,超时重传,连接管理,流量控制,拥塞控制 |
提升性能 | 滑动窗口,延迟应答,捎带应答,快速重传 |
这里只小结了上述介绍的,肯定是不全面的,读者可以自行拓展学习
UDP实现TCP
引导:只要从TCP实现可靠性方面思考即可
UDP想实现TCP,无非就是实现TCP的可靠性,所以我们可以给UDP加入确认应答(确认号,序列号),超时重传,流量控制即可初步实现TCP;若要提升性能可以加入滑动窗口,延迟应答,捎带应答,快速重传。
总结
TCP和UDP的区别
从两者的特点展开比较
-
有/无连接
UDP:多用于DNS,DHCP,视频,多媒体通信
TCP:多用于HTTP/HTTPS -
面向数据报/数据流
数据报无边界,易丢包,乱序
数据流有边界,可靠,有序 -
有无可靠性
从报头可区分
UDP:8字节大小,4个字段,短小精干。
TCP:20字节大小,包含各种实现可靠性和效率的字段。(这里又可以展开补充区别)