UDP
- 特性: 无连接, 不可靠, 面相数据报
- 面相数据报: 传输数据有大小限制, 并且向上层交付数据时是整条整条交付
- 无连接不可靠 : 只要知道对端地址就可以发送数据 , 且不关心是否丢失数据
- 协议字段: 16位源端口, 16位目的端口, 16位数据报长度, 16位校验和
- 16位源端口
- 16位校验和-- 校验接收到的数据和发送的数据是否完全一致
- 二进制反码求和算法:
假设原数据为1100, 1010, 0000(校验位) , 那么把他们按照4bit一组进行按位取反相加 , 1100->0011, 1010-> 0101 ,校验位的计算就是0011加上0101->1000. 填入校验位; 所以发送的数据为1100 , 1010, 1000 ; 收到数据后同样进行按位取反相加。0011+0101+0111 =1111;全为1表示正确 。 等于是 自己加上自己的取反, 那么 结果肯定应该是全1 。如果传输正确的话
- 二进制反码求和算法:
- 16位数据报长度
- UDP数据报最大长度不能超过64k-8 —65535
- 若传输的数据大小过大需要在应用层进行分包操作, 并且udp不保证数据有序到达, 因此需要在应用层进行包序管理
- sendto(buf, len)
- udp协议对发送的数据直接封装头部, 然后发送出去
- 若数据大于64k-8 则需要用户在应用层进行分包操作
- udp并不保证可靠, 也不保证包序, 因此分包后还需要在应用层进行包序管理
- 面相数据报决定了udp数据报只能整条接收, 整条发送
- UDP数据报最大长度不能超过64k-8 —65535
- UDP应用: 不能用于安全性要求的传输, 但是因为报头短, 不需要保证可靠传输, 因此传输速率很快, 因此可以用于实时性要求高的场景
- UDP在协议栈层面实现了广播功能 : 通过向一个IP地址发送数据, 实现将数据发送到局域网所有主机上
- 在传输层基于UDP实现的应用层协议:
- DHCP协议,: 动态主机配置协议
- DNS协议: 域名解析协议
- NFS: 网络文件系统
- TFTP: 简单文件传输协议
- UDP缓冲区:
- 发送缓冲区: 作用并没有太大体现, 因为sendto发送数据都是直接封装头部整条发送
- UDP如何实现科考船是:
- 引入序列号 :保证数据顺序;
- 引入确认应答机制, 确保对端受到数据
- 引入超时重传机制, 如果隔一段世界对方没有应答则重发数据
TCP
-
特性: 面向连接, 可靠传输, 面向字节流传输服务
-
协议字段:
- 16位源/目的端口: 负责端与端之间的数据传输
- 32位序号/确认序号: 进行TCP协议栈中的包序管理+ 其他功能
- 4位首部长度: 长度单位是4 个字节 , 最大长度: 15*4 最小长度/有效长度 20
- 定义数据包头部长度
- 6位保留位
- 6位标识位:
- URG-紧急指针/
- ACK-确认服务/
- PSH/RST–重置链接报文/
- SYN-连接建立请求/
- FIN-断开连接请求
- 16位窗口大小: 滑动窗口机制
- 16校验和:-校验接收到的数据与发送的数据是否完全一致
- 16位紧急指针: 带外数据–调研—通常数据放在队列—紧急指针提高数据优先级
- 40字节选项数据: 可有可无 是一个额外数据,用到时就会有数据
-
具体协议特性的实现
- 面向连接: TCP的连接管理—通过三次握手和四次挥手实现连接建立与断开
-
两端socket进行状态标记, 标记每一个状态该做什么
- 客户端:SYN_SENT->ESTABLISHED FIN_WAIT1->FIN_WAIT2->TIME_WAIT->CLOSED
- 服务端: SYN_RCVD->ESTABLISHED CLOSE_WAIT->LAST_ACK->CLOSED
-
为什么连接是三次握手链接而不是两次或者三次
- 确保两端都有数据收发能力
第一次握手:客户端确定服务端有接收数据能力, 第二次握手客户端确认服务端也有接收能力 , 第三次是服务端为了防止前两次连接后过了很久链接可能断开/或者是已经失效的链接, 或者出现其他问题所以进行第三次探测.
- 确保两端都有数据收发能力
-
为什么挥手是四次而不是三次
- A发送SYN报文给B—第一次报文交互
-
TIME_WAIT有什么用?不要他可以吗?
- 如果没有TIME_WAIT会出现什么问题
- 没有TIME_WAIT的话closed会直接释放socket, 不再占用地址信息, 若立即启动一个新客户端, 使用相同的端口服务发送SYN数据, 但若上一次挥手时最后一次的ACK丢失则服务端则处于LAST_ACK状态(立即使用是相同地址启动客户端可能会造成影响)
- 客户端发送建立连接请求时发送的SYN会被服务端认为状态错误, 并重置链接
- 客户端有可能收到服务端重发的FIN包
- 等待的目的/作用
- 对重传FIN包作出处理
- 等待重传的数据不会对新连接造成影响
- 等待多久比较合适: 等待两个MSL
- MSL: 报文最大生命周期
- 报文要不然被对端接收, 要不就消失在网络中
- MSL: 报文最大生命周期
- 为什么等待两个MSL
- MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);
- 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK)
- 没有TIME_WAIT的话closed会直接释放socket, 不再占用地址信息, 若立即启动一个新客户端, 使用相同的端口服务发送SYN数据, 但若上一次挥手时最后一次的ACK丢失则服务端则处于LAST_ACK状态(立即使用是相同地址启动客户端可能会造成影响)
- 如果没有TIME_WAIT会出现什么问题
-
setsockopt(int sockfd, int level , int name , int opt , int opt_len)
- 获取或者设置与某个套接字关联的选项。选项可能存在于多层协议中,它们总会出现在最上面的套接字层。当操作套接字选项时,选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该 将层的值指定为SOL_SOCKET。
- SO_REUSEADDR
- int opt = 1 开启地址重用选项
- opt_len = sizeof(opt)
- level: 选项所得协议层
-
TCP协议栈中的保活机制
- 默认情况下7200s双方没有数据往来, 则相对发发送包活探测数据报, 要求对方响应
- 若得到响应则认为正常
- 若连续9次没有得到响应则认为连接断开, 将socket状态置位CLOSE_WAIT
-
- 可靠传输: TCP为了可靠性 , 牺牲了部分性能
- 面向连接
- 确认应答机制
TCPd将没个字节的数据都进行了编号,即为序列号 - 超时重传机制-----超时等待时间是动态规划的, 根据自身网络状况来规划
如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发;
但是, 主机A未收到B发来的确认应答,也可能是因为ACK丢了 - 协议字段中的序号/确认序号----包序管理
- 校验和
- 如何避免丢包,以及如何提高性能
- 滑动窗口机制
由于对每发送一个数据段,都要给一个ACK确认应答, 收到ACK后在发送下一个数据段. 这样做有一个较大的缺点就是性能会较差, 尤其是数据往返的时间较长, 这时就引出了滑动窗口机制, 一次发送多条数据(将多个等待时间压缩在一起)- 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值
- 发送前四个段的时候, 不需要等待任何ACK, 直接发送;
- 收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推
- 操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉;
- 窗口越大, 则网络的吞吐率就越高;
- 对发送方来说: 后延的移动取决于是否收到ack确认回复; 前沿移动取决于窗口大小
- 对接收方来说:后延的移动取决于是否收到数据; 取决于接收方的接收缓存区剩余空间
- 流量控制: 接收方通过每次收到数据后的确认回复, 向对端传递窗口大小, 限制对方最多能发送的数据,便面发送数据过多, 导致接收缓冲区满溢后数据丢包
- 如果出现丢包, 如何进行重传 —快速重传机制
- 在连续的数据发送中,若接收方接收到了第二条数据但是没接收到第一条,则认为第一条数据可能丢失,则向发送方发送第一条数据重传请求,并且连续发送三次
- 若发送方连续收到三条指定数据的重传请求, 则对这条数据进行重传, 三次是为了避免网络延迟而进行重传
- 当某一段报文段丢失之后, 发送端会一直收到 1001 这样的ACK, 就像是在提醒发送端 "我想要的是 1001"一样;
- 如果发送端主机连续三次收到了同样一个 “1001” 这样的应答, 就会将对应的数据 1001 - 2000 重新发送;
- 这个时候接收端收到了 1001 之后, 再次返回的ACK就是7001了(因为2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中;
- 拥塞控制机制—提高性能
虽然有了滑动窗口机制, 能够高效可靠的发送大量数据, 但是如果在刚开始阶段就发送大量数据, 仍然可能引发问题, 因为网络上很多计算机可能当前的网络状况以及很拥堵了, 在不清楚网络状况下, 贸然发送大量数据是很危险的, 所以这时又引进了新机制-----拥塞控制
- 慢启动, 快增长 避免网络状况不好导致的大量丢包
- 当TCP开始启动的时候, 慢启动阈值等于窗口最大值;
- 在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1;
- 少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;
- 当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
- 拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案.
- 延迟应答–提高性能
延迟等待时间一般不会超过500ms- 接收数据之后并不立即回复
- 因为立即回复确认会使窗口变小, 网络吞吐率降低, 传输速率降低
- 等待一段时间, 这一段时间很有可能将用户数据取出, 尽可能保障窗口大小
- 捎带应答
- 再接收到数据之后, 若接收方也要发送数据, 则将确认回复和要发送的数据合成一个数据包发送出去
- 滑动窗口机制
- 面向字节流
- tcp调用接口send发送数据, 是将数据放到发送的缓冲区中;操作系统选择合适的时机取出合适的大小(MSS),将指定长度的数据发出
- 发送的多条数据在缓冲区中会有可能当做一条数据来进行发送 ,接收方, 接收数据放在缓冲区中, 缓冲区中的多条数据可能一次 recv 就全部取出 , 也有可能将多条数据当做一条数据进行处理
- 对于recv/send接口所发送/接收的数据大小,没有大小的限制,传输灵活, 但是tcp在传输数据是会存在数据粘包问题
- 多条数据在发送缓冲区/接收缓冲区种合成一条数据
- 原因: tcp在传输数据时对数据的边界不敏感-----本质原因
- 解决方案:在应用层进行数据边界管理
- 在不定长数据的应用层协议头中定义数据长度------UDP不会产生粘包问题–
- 对于变长的包, 还可以在包和包之间使用明确的分隔符(应用层协议, 是程序猿自己来定的, 只要保证分隔符不和正文冲突即可–特殊字符
- 对于定长的包, 保证每次都按照固定大小读取即可
- 面向连接: TCP的连接管理—通过三次握手和四次挥手实现连接建立与断开