TCP小结
为什么TCP这么复杂? 因为要保证可靠性, 同时又尽可能的提高性能.
可靠性:
校验和
在TCP头部有一个16位的校验和,通过这个校验和来验证数据是否完整
序列号(按序到达)
数据按照分包范围,但是从第一个包到最后一个包的序列号是连续的,seq 1 ~ 1000
确认应答
当对端收到数据包后,会回复确认收到的包,ack = seq + 1
超时重发
当一端发送数据后没有收到确认应答的话,便会在一定时间后再发送一次,无论对端有没有收到数据报,所以对端一定要有分辨那些包是已经发送过的
连接管理
连接时要三次握手,断开连接时要四次挥手
流量控制
在创建一个tcp socket之后,会在内核创建一个接收缓冲区和发送缓冲区,每次回复确认应答的时候会在TCP协议头带上窗口大小,但是这个窗口大小并不是实际可以接受的大小,在选项中还有窗口扩大因子M,实际可以接受的大小是,窗口大小的值左移M位,当窗口为0时,对端便不能再发送数据,但是每隔一段时间会发送一个窗口大小探测包
拥塞控制
又名慢启动,在刚开始发送数据时只发送很少的一点数据,检测网络环境,摸清环境后再决定一个适合的大小发送数据,
提高性能:
滑动窗口
发送数据时并不等待第一条数据确认应答回来后再发送第二条,而是直接发送一个窗口大小的多条数据,根据确认应答来决定是否重传或者移动窗口
快速重传
当某一段报文段丢失之后, 发送端会一直收到 1001 这样的ACK, 就像是在提醒发送端 "我想要的是 1001"一样;
如果发送端主机连续三次收到了同样一个 "1001" 这样的应答, 就会将对应的数据 1001 - 2000 重新发送;
这个时候接收端收到了 1001 之后, 再次返回的ACK就是7001了(因为2001 - 7000)接收端其实之前就已 经收到了, 被放到了接收端操作系统内核的接收缓冲区中;
延迟应答
在服务端有一个接收缓冲区,当服务端接收到一个数据后,并不会立即返回剩余窗口大小,而是等一会,这个时间不会死等,而是每个多少个包或多长时间后发送可以接受的窗口大小
捎带应答
因为每次数据的收发基本上都是一收一发的,所以有时候ACK可以搭data的便车,不用再单独起一个数据包返回ACK
其他:
定时器(超时重传定时器, 保活定时器, TIME_WAIT定时器等)
TCP如何解决数据粘包问题
- 可以每次接收定长的数据,就如同req一样,接收sizeof(req)大小的数据
- 如果是不定长的数据,可以每次在TCP头部加上数据的总长度
- 面对不定长的数据,还可以在每段数据之间加上分割符,只要不影响原文数据即可
UDP实现可靠传输
- 可以引入确认应答机制,每收到一个数据报,便返回一个确认应答
- 引入序列号,保证数据顺序
- 引入超时重传,如果每隔一段时间没有应答,则重传
其实实现UDP的可靠传输就是借助TCP可靠传输的思想,实现TCP的数据粘包问题也可以借助UDP的思想
那么TCP和UDP哪个好呢
这个回答无论你怎么选都是错误的,要结合具体情境来看
UDP适合实时性要求较高,例如视频,直播等
TCP适合可靠传输,例如文件传输,重要状态更新等
listen的第二个参数
Linux内核协议栈为一个tcp连接管理使用两个队列:
1. 半链接队列(用来保存处于SYN_SENT和SYN_RECV状态的请求)
2. 全连接队列(accpetd队列)(用来保存处于established状态,但是应用层没有调用accept取走的请求)
而全连接队列的长度会受到 listen 第二个参数的影响.
全连接队列满了的时候, 就无法继续让当前连接的状态进入 established 状态了.
这个队列的长度是 listen 的第二个参数 + 1.