赞
踩
目录
ACK:应答;确认号是否有效
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
SYN:请求建立连接;我们把携带SYN标识的称为同步报文段
FIN:申请断开连接;通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段
数据:TCP将每个字节的数据都进行了编号,并使用32位序号保存,即为序列号。
确认应答:ACK标志位置为1,会使用32位确认序号保存
下一个是多少?
接收到的数据报,连续序号的最大值+1
隐藏含义:接收端下一个的序号,告诉发送者之前的数据报,全部都接收到了
- 主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B;
- 如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发;
因此主机B会收到很多重复数据。那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉。
在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接。
建立连接:本质是双方保存了一个连接状态
断开连接
客户端接收到第三个数据报,不能马上置为closed
原因:第四个数据报可能出现丢包(服务器无法断开连接),服务端就会根据超时重传机制, 重发第三个数据报,此时客户端如果是closed,就没法接受
服务端状态转化:
- [CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态,等待客户端连接;
- [LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送SYN确认报文。
- [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了。
- [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close),服务器会收到结束报文段,服务器返回确认报文段并进入CLOSE_WAIT;
- [CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入
- LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
- [LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK,彻底关闭连接。
- [CLOSED -> SYN_SENT] 客户端调用connect,发送同步报文段;
- [SYN_SENT -> ESTABLISHED] connect调用成功,则进入ESTABLISHED状态,开始读写数据;
- [ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时,向服务器发送结束报文段,同时进入FIN_WAIT_1;
- [FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,
- 开始等待服务器的结束报文段;
- [FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段,进入TIME_WAIT,并发出LAST_ACK;
- [TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life,报文最大生存时间)的时间,才会进入CLOSED状态。
下图是TCP状态转换的一个汇总:
- 较粗的虚线表示服务端的状态变化情况;
- 较粗的实线表示客户端的状态变化情况;
- CLOSED是一个假想的起始点,不是真实状态;
问题一:双方的连接状态,为什么最后才是closed?
答:双方都要保证可靠的关闭连接
问题二:第二、三个数据报为什么没有合并?
答:第二个数据报,是系统内核返回的(不用程序写代码来发送)
第三个数据报,是程序调用closed方法发送的
问题三:服务器中出现大量closed_wait的原因?
答:服务端没有执行close方法(因为执行close才会发送第三个数据报)
作用:以并行的方式发送数据报,减少等待时间,提高效率
- 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图的窗口大小就是4000 个字节(四个段)。
- 发送前四个段的时候,不需要等待任何ACK,直接发送;
- 收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据;依次类推;
- 操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从缓冲区删掉;
- 窗口越大,则网络的吞吐率就越高;
发生的丢包的情况:
- 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段,通过ACK端通知发送端;
- 窗口大小字段越大,说明网络的吞吐量越高;
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;
- 发送端接受到这个窗口之后,就会减慢自己的发送速度;
- 如果接收端缓冲区满了,就会将窗口置为0;这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。
- 此处引入一个概念程为拥塞窗口
- 发送开始的时候,定义拥塞窗口大小为1;
- 每次收到一个ACK应答,拥塞窗口加1;
- 每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口;
接收端返回流量窗口代表接收缓冲区的可用空间大小,如果立即返回,就不划算
接收端返回的流量窗口,不是立即返回,而是等待一定时间,这样返回的流量窗口大小就可能更大
不管是客户端还是服务端,接收到数据后,返回的ack应答包(作为接收端),可以和发送的数据报(作为发送端)再一起(捎带的方式)发送给对方
- 首先要明确,粘包问题中的 "包" ,是指的应用层的数据包。
- 在TCP的协议头中,没有如同UDP一样的 "报文长度" 这样的字段,但是有一个序号这样的字段。
- 站在传输层的角度,TCP是一个一个报文过来的。按照序号排好序放在缓冲区中。
- 站在应用层的角度,看到的只是一串连续的字节数据。
- 那么应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分,是一个完整的应用层数据包。
- 对于定长的包,保证每次都按固定大小读取即可;例如上面的Request结构,是固定大小的,那么就从缓冲区从头开始按sizeof(Request)依次读取即可;
- 对于变长的包,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置;
- 对于变长的包,还可以在包和包之间使用明确的分隔符(应用层协议,是程序猿自己来定的,只要保证分隔符不和正文冲突即可);
- 对于UDP,如果还没有上层交付数据,UDP的报文长度仍然在。同时,UDP是一个一个把数据交付给应用层。就有很明确的数据边界。
- 站在应用层的站在应用层的角度,使用UDP的时候,要么收到完整的UDP报文,要么不收。不会出现"半个"的情况。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。