UDP / TCP
UDP 协议
UDP的特点
-
无连接 一旦得知接收端的IP和端口号就可以加个报头直接向下层交付进行传输,不需要建立连接。 -
不可靠 没有确认、重传机制。无法得知也不关心报文是否到达接收端。 -
面向数据报 不能够灵活的控制读写数据的次数和数量。
应用层交给UDP多长的报文,UDP原样发送,既不会拆分,也不会合并。 用UDP传输100个字节的数据: 如果发送端调用一次sendto,发送100个字节,那么接收端也必须调用对应的一次recvfrom,接收100个字节;而不能循环调用10次recvfrom,每次接收10个字节。
UDP协议格式
LInux内核UDP报头结构体
校验和:如果校验和错误,直接丢弃。
16位UDP长度: 报头和数据的总大小。
标识UDP长度的字段只有16位,也就是说一个UDP能传输的数据最大长度是64K(包含UDP首部)。如果我们需要传输的数据超过64K,就需要在应用层手动的分包,多次发送,并在接收端手动拼装。
UDP的缓冲区
- UDP没有类似TCP的发送缓冲区。调用sendto接口发送数据会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作
- UDP具有接收缓冲区,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致;如果缓冲区满了,再到达的UDP数据就会被丢弃。
UDP的socket既能读,也能写。是全双工通信。
TCP 协议
传输控制协议(TCP,Transmission Control Protocol)
TCP 协议报头
- 源/目的端口号:表示数据由发送端的应用程序发送到接收端的哪个进程。
- 32位序列号:TCP对发送的数据进行编号,主要解决了乱序和重复报文的问题。
- 32位确认序列号:主要解决了丢包。
- 4位首部长度:表示该头部有多少个32位bit (有多少个4字节;所以TCP头部最大长度是15*4= 60 byte)
- 6位标志位:
URG:紧急指针是否有效 ACK:确认号是否有效 PSH:催促接收端应用程序立刻从TCP缓冲区把数据读走 RST:对方要求重新建立连接,我们把携带RST标识的称为复位报文段 SYN:请求建立连接,携带SYN标识的称为同步报文段 FIN:通知接收方断开连接,我们称携带FIN标识的为结束报文段
- 16位窗口大小:发送端的接收缓冲区大小,保证流量控制
- 16位校验和:发送端填充,CRC校验.接收端校验不通过,则认为数据有问题。(包含爆报头和数据)
- 16位紧急指针:标识哪部分数据是紧急数据 (紧急数据一次只能传输一字节)
确认应答(ACK)机制
TCP将每个字节的数据都进行了编号,即序列号。
每一个ACK都带有对应的确认序列号。告知发送端已收到确认序列号之前的数据,下次从确认序列号开始发送。
超时重传机制
当报文在传输的途中由于网络问题或校验出错,接收端未向发送端发送ACK报文,发送端在等待一定时间就会向接收端重新发送报文。
还有一种情况,接收端回应的ACK报文丢失了。此时发送端在超时后进行重传,接收端收到重复报文,这里利用报头中的序列号可以轻易做到去重的效果。
超时的时间:
超时时间应该随着网络环境随之变化。 如果超时时间太长,会影响重传效率,超时时间太短,可能会频繁发送重复的报文。 TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间。 Linux中(BSD Unix和Windows也是如此)超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍. 如果重发一次之后,仍然得不到应答,等待2500ms后再进行重传. 如果仍然得不到应答,等待4500ms进行重传.依次类推,以指数形式递增.累计到一定的重传次数, TCP认为网络或者对端主机出现异常,强制关闭连接。
三次握手 / 四次挥手
三次握手
服务器状态变化:
[CLOSED -> LISTEN]服务器端调用listen后进入LISTEN状态,等待客户端连接 [LISTEN -> SYN_RCVD]一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送SYN确认报文. [LISTEN -> ESTABLISHED] 收到客户端的ACK后进入该状态,连接成功建立。
四次挥手:
CLOSE_WAIT 状态:
如果服务器上出现大量的CLOSE_WAIT状态,可能是服多器没有调用close函数关闭socket返回的文件描述符,导致四次挥手没有正确完成。
TIME_WAIT 状态:
作用:
- 等待数据在网络上消散,可能出现网络拥堵的情况,导致报文还未到达目标主机,而连接先已经关闭。
- 可能出现ACK丢包,等待接收端重发FIN报文,保证连接正确关闭。
查看 TIME_WAIT 等待时长
TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态等待两个MSL(maximum segment lifetime) 的时间后才能回到CLOSED状态
MSL是TCP报文的最大生存时间。TIME_WAIT持续存在2MSL的时长,可以保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,可能会收到来自上一个进程的迟到的教据,但是这种数据很可能是错误的)。 同时也是在理论上保证最活一个报文可靠到达(假设最后一个ACK丢失,那么服务器会再重发一个FIN.这时虽然客户端的进程不在了,但是TCP连还在,仍然可以重发LAST_ACK)。
使服务器立马可以重启并绑定原端口号,不需要等待 TIME_WAIT 结束。
滑动窗口
窗口大小指的是无需等待确认应答而可以继续发送数据的最大值.上图的窗口大小就是4000个字节(四个段) 发送前四个段的时候,不需要等待任何ACK,直接发送。 收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据。依次类推。 操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答。只有确认应答过的数据,才能从缓冲区删除。 窗口大小受限于接收端接收缓冲区大小。 ·窗口越大,网络的吞吐率越高。
丢包处理:
如果是ACK报文丢失了,并不影响,可以通过后续的ACK确认报文验证。
如果是报文丢失了,发送端会一直收到重复的ACK。
如果发送端主机连续三次收到了相同的ACK应答,发送端就将对应的数据重新发送。此时接收端收到了重发报文后,再次返回的ACK就是7001了。
这种机制被称为"快重传”。
流量控制
接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。 因此TCP支持根据接收端的处理能力,来决定发送端的发送速度,这个机制就叫做流量控制(Flow Control) 接收端将自己可以接收的缓冲区大小放入TCP首部中的窗口大小”字段,通过ACK端通知发送端;窗口大小字段越大。说明网络的香吐量越高; 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;发送端接受到这个窗口之后,就会减慢自己的发送速度; 如果接收端缓冲区满了,就会将窗口置为0。这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
TCP报头中有一个16位窗口字段,存放了发送方接收缓冲区窗口大小。 TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位。
拥塞控制
TCP引入“慢启动”机制,先发送少量的数据,再逐步增加发送量,探清当前的网络状态,再决定按照多大的速度传输数据。
-
开始发送时,定义拥塞窗口大小为1每次收到一个ACK应答,拥塞窗口加1。 -
每次发送数据包时,将拥塞窗口和接收端主机的接收缓冲区大小做比较,取较小的值作为实际发送的窗口。 -
拥塞窗口的增长速度是指数级别的,初使比较慢,,但是增长速度非常快。 -
增长到一定程度会遇到阈值,由指数级增长变成线性增长。
·一开始,慢启动阈值等于窗口最大值。 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1。少量的丢包,我们仅仅是触发超时重传。大量的丢包,我们就认为网络拥塞。 当TCP通信开始后,网络吞吐量会逐渐上升。随着网络发生拥堵,吞吐量会立刻下降。 拥塞控制是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。
延迟应答
如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小。 假设接收端缓冲区为10000字节。一次收到了2000字节的数据,如果立刻应答,返回的窗口就是8000字节,但实际上可能处理端处理的速度很快, 10ms之内就把2000字节的数据从缓冲区取走了。 在这种情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来;如果接收端稍微等一会再应答,比如等待200ms再应答,那么这个时候返回的窗口大小就是10000字节。
延迟应答受到以下条件限制:
数量限制:每隔N个包应答一次 时间限制:超过最大延迟时间应答一次
具体的数量和超时时间,依操作系统不同也有差异。一般N取2,超时时间取200ms;
捎带应答
大多数情况,客户端服务器在应用层也是"一发一收”的,意味着客户端向服务器发送报文,服务器也会给客户端回一个报文。 那么这个时候ACK就可以和数据一起发送,而不必单独发送。
面向字节流
|