TCP
在TCP/IP协议模型中, 传输层协议有TCP和UDP, 这里主要介绍下可靠传输TCP协议, 目前是传输层协议首选.
特点
- 面向数据流(字节流形式)
- 虚电路连接
- 有缓冲传输(提供push机制 )
- 无结构数据流(无边界)
- 全双工
连接建立
socket接口使用 connect()时建立连接, 采用三次握手, 请看下图 :
在这个过程中完成了几个重要功能 :
- 建立连接, 做好传送数据准备.
- 协商各自报文段初始序号ISN( 任意选取, TCP准规定不可为1, 其中一个原因是避免IP欺骗).
- 协商报文段最大长度MSS.
为什么要三次握手 ?
考虑正常情况: 主机A给主机B发送数据包, 在网络中丢失了. 主机A定时器到期后未收到来自主机B的确认, 于是A重新发送数据包, 主机B收到再发送确认. 如果之前的数据包没有丢失呢 ? 只是延迟了比较长的时间. 那么这个失效的数据包又重新发给主机B, 主机B以为A要建立连接, 会给A发送确认, 同意建立连接. A肯定不会理它了, 所以主机B一直在那白白地等A传送数据, 所以采用三次握手能很好解决这个问题. 其他精彩回答可参考 https://www.zhihu.com/question/24853633
可靠传输
1.防止丢失
如果限定一个时间之内未收到报文段的确认, 则认为该报文丢失, 进行重传. 超时定时器设定采用自适应动态算法.
2.防止重复和乱序
采用唯一序号机制. 这种方法相当于给每个字节编号, 体现了TCP面向数据流的特征
3.确认机制
1.确认指明期望收到下个报文段的序号, 而不是已经收到的报文段序号.
2.累计确认(确认信息报告已经累积了多少个字节的数据).
3.捎带确认(不单独发送确认信息, 捎带在自己发送的报文段中).
拥塞控制
慢启动算法: 发送方维持一个拥塞窗口cwnd的变量. cwnd取决于网络拥塞情况动态变化, 也可以小于对方接收窗口. 开始不发送大量数据, 先发送试探报文, 一步步增大cwnd的值.
为防止cwnd过大导致网络拥塞, 需要设置一个慢开始门限值ssthresh :
当cwnd小于ssthresh时, 使用慢开始算法.
当cwnd等于ssthresh时, 使用慢开始算法或拥塞避免算法都可以.
当cwnd大于ssthresh时, 使用拥塞避免算法.
拥塞避免算法 : 每进过一个RTT就把cwnd+1, 而不是加倍, 这样使得拥塞窗口线性增长.
无论是在慢开始阶段还是在拥塞避免阶段, 只要发送方判断网络出现拥塞(未收到确认, 可能是丢失了, 也视为拥塞), 就把慢开始门限减小到拥塞窗口cwnd的一半, 并设置拥塞窗口大小为1, 执行慢开始算法.
这里拥塞窗口的单位应该是字节数.
快重传和快恢复
要求接受方在收到一个失序的报文后立即发送重复确认, 而发送方在连续接收三个重复确认时要重传失序报文, 不必等待设置的超时计数器到期.
在发送方收到三个重复确认以后, 执行乘法减小算法, 将门限值ssthresh减小到一半.
在网络拥塞情况下, 有可能接收方收不到三个重复确认, 这是发送方认为网网络没有出现拥塞, 把cwnd设置为ssthresh大小, 然后执行拥塞避免算法.
连接关闭
断开连接使用四次挥手, 在客户端调用socket接口 close() 后发送FIN数据包, 参考下图:
TCP连接是全双工的, 两个方向上都要关闭, 原则是发起关闭的一方发送FIN字段, 另一方接收到后必须发送关闭的确认. 从图中可以看出, 当收到FIN并不是立即关闭, 它是先发送确认, 然后再继续发送自己的FIN请求.
需要注意的是 当客户端收到关闭确认ACK之后, 它仍然可以接受服务器的数据, 并且向服务器发送确认信息, 一个仅包含确认信息的报文不占用序号.
客户端最后发送ACK后进入TIME_WAIT状态而不是CLOSED, 因为最后的这个ACK可能丢失, 如果是CLOSED话就已经断开连接了. 服务器方的这个连接关闭就会出错. 所以这里应该等待一段时间再进入CLOSED状态, 这个时间就是最大报文生存周期(MSL).
异常关闭: 发送RST报文段, 立即停止传输, 关闭连接, 释放资源.
半开放连接: 引入保活定时器, 指点时间之内仍然没有数据通信, 则发送侦查报文, 根据不同情况进行处理.