Linux网络---传输层协议TCP(四) 1、拥塞控制衡量报文在网络中的情况就是拥塞控制。虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据.但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题. 因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵.在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的.TCP引入慢启动机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据;- 此处引入一个概念称为拥塞窗口- 发送开始的时候, 定义拥塞窗口大小为1;- 每次收到一个ACK应答, 拥塞窗口加1;- 每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口;- 为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍。- 此处引入一个叫做慢启动的阈值- 当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长当 TCP 开始启动的时候慢启动阈值等于窗口最大值在每次超时重发的时候慢启动阈值会变成原来的一半同时拥塞窗口置回 1;少量的丢包我们仅仅是触发超时重传大量的丢包我们就认为网络拥塞当 TCP 通信开始后网络吞吐量会逐渐上升随着网络发生拥塞吞吐量会立刻下降拥塞控制归根结底是 TCP 协议想尽可能快的把数据传输给对方但是又要避免给网络造成太大压力的折中方案.TCP 拥塞控制这样的过程就好像热恋的感觉2、延迟应答如果接收数据的主机立刻返回ACK应答这时候返回的窗口可能比较小。- 假设接收端缓冲区为1M.一次收到了500K的数据; 如果立刻应答返回的窗口就是500K;- 但实际上可能处理端处理的速度很快10ms之内就把500K数据从缓冲区消费掉了;- 在这种情况下接收端处理还远没有达到自己的极限即使窗口再放大一些也能处理过来;- 如果接收端稍微等一会再应答比如等待200ms再应答那么这个时候返回的窗口大小就是1M; 一定要记得窗口越大网络吞吐量就越大传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率; 那么所有的包都可以延迟应答么? 肯定也不是;- 数量限制: 每隔N个包就应答一次; - 时间限制: 超过最大延迟时间就应答一次; 具体的数量和超时时间依操作系统不同也有差异; 一般N取2超时时间取200ms;3、捎带应答在延迟应答的基础上我们发现很多情况下客户端服务器在应用层也是一发一收的。意味着客户端给服务器说了 How are you服务器也会给客户端回一个 Fine, thank you; 那么这个时候ACK就可以搭顺风车和服务器回应的 Fine, thank you 一起回给客户端4、面向字节流创建一个TCP的socket同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;- 调用write时数据会先写入发送缓冲区中; - 如果发送的字节数太长会被拆分成多个TCP的数据包发出;- 如果发送的字节数太短就会先在缓冲区里等待等到缓冲区长度差不多了或者其他合适的时机发送出去;- 接收数据的时候数据也是从网卡驱动程序到达内核的接收缓冲区;- 然后应用程序可以调用read从接收缓冲区拿数据;- 另一方面TCP的一个连接既有发送缓冲区也有接收缓冲区那么对于这一个连接既可以读数据也可以写数据. 这个概念叫做 **全双工** 由于缓冲区的存在TCP程序的读和写不需要一一匹配例如: - 写100个字节数据时可以调用一次write写100个字节也可以调用100次write每次写一个字节;- 读100个字节数据时也完全不需要考虑写的时候是怎么写的既可以一次read 100个字节也可以一次read一个字节重复100次;5、粘包问题- 首先要明确粘包问题中的包是指的应用层的数据包.- 在TCP的协议头中没有如同UDP一样的报文长度这样的字段但是有一个序号这样的字段. - 站在传输层的角度TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中.- 站在应用层的角度看到的只是一串连续的字节数据.- 那么应用程序看到了这么一连串的字节数据就不知道从哪个部分开始到哪个部分是一个完整的应用层数据包.那么如何避免粘包问题呢? 归根结底就是一句话明确两个包之间的边界.1- 对于定长的包保证每次都按固定大小读取即可;例如上面的Request结构是固定大小的那么就从缓冲区从头开始按sizeof(Request)依次读取即可;- 2对于变长的包可以在包头的位置约定一个包总长度的字段从而就知道了包的结束位置;- 对于变长的包还可以在包和包之间使用明确的分隔符(应用层协议是程序猿自己来定的只要保证分隔符不和正文冲突即可);思考: 对于UDP协议来说是否也存在粘包问题 呢?- 对于UDP如果还没有上层交付数据UDP的报文长度仍然在. 同时UDP是一个一个把数据交付给应用层. 就有很明确的数据边界. - 站在应用层的站在应用层的角度使用UDP的时候要么收到完整的UDP报文要么不收. 不会出现半个的情况.6、TCP异常情况进程终止进程终止会释放文件描述符仍然可以发送 FIN. 和正常关闭没有什么区别.机器重启和进程终止的情况相同.优先关闭进程机器掉电 / 网线断开接收端认为连接还在一旦接收端有写入操作接收端发现连接已经不在了就会进行 reset. 即使没有写入操作TCP 自己也内置了一个保活定时器会定期询问对方是否还在。如果对方不在也会把连接释放.机器掉电 / 网线断开掉线一方无法发出 FIN、RST 任何报文。对端此时看不到断开信号认为连接仍然存活半打开连接。两种后续结果若对端发送数据重传多次失败触发 RST连接断开。若对端一直不发数据连接僵住依靠TCP 保活定时器周期性探测对方探测失败后释放连接默认探测周期很长。TCP 自带保活检测速度慢想要快速发现掉线需要应用层心跳。另外应用层的某些协议也有一些这样的检测机制。例如 HTTP 长连接中也会定期检测对方的状态。例如 QQ在 QQ 断线之后也会定期尝试重新连接.// linux kernel include/linux/tcp.hstructtcphdr{__be16 source;__be16 dest;__be32 seq;__be32 ack_seq;#ifdefined(__LITTLE_ENDIAN_BITFIELD)__u16 res1:4,doff:4,fin:1,syn:1,rst:1,psh:1,ack:1,urg:1,ece:1,cwr:1;#elifdefined(__BIG_ENDIAN_BITFIELD)__u16 doff:4,res1:4,cwr:1,ece:1,urg:1,ack:1,psh:1,rst:1,syn:1,fin:1;#else#errorAdjust your asm/byteorder.h defines#endif__be16 window;__sum16 check;__be16 urg_ptr;};