报文的理解
如果应用层正在进行报文的解析,处理,会不会影响OS从网络中读取报文?为什么?
不会,首先,一个进程不是要运行到退了cpu才能运行其他的进程。进程有对应的时间片,时钟中断一直在进行,cpu接受到中断,cpu转而处理OS内置的中断向量表中对应的中断处理方法,进行进程调度,检查时间片,时间片耗尽了,进程会被切换。进程切换进程task_struct会保留上下文数据,不会影响下一个进程的执行。
在OS内部,一定可能会同时存在大量的报文,而这些报文,有的在传输层,有的在网络层,OS就必须管理这些报文 -> 先描述,在组织
struct sk_buff是描述报文的结构体。
head成员:指向数据区的头部
end成员:指向数据区的尾部
tail:指向应用层数据的尾部
data:指向的是****当前所在层协议报头头部。
所谓的解包和分用,本质就是移动data指针在缓冲区中的指向(****+-对应协议层报头的长度)
TCP结构
标准问题:
报头和有效载荷怎么分离?
TCP是不定长报头,其可以携带对应的选项,不带选项报头为20字节。其结构中有4位首部长度,4位比特位取值:[0,15],怎么表达20及以上字节呢?4位比特位,基本单位是4字节,能表示的字节数取值:[0,60],但由于TCP最少都是20字节,取值范围是[20,60],4位比特位取值范围[5,15]。
如何交付?16位的目的端口号
怎么没有报文大小?只有报头大小
TCP不需要也不能设置报文大小。TCP是面向字节流的,传输层只要能把报文进行解包分用传给应用层的接收缓冲区就行了,具体处理字节流,应用层怎么保证读到一个完整报文,由应用层来做。
那么既然不清楚TCP报文大小,有没有可能struct sk_buff中同时存在两个TCP报头,黏在一起了?不可能,一个struct sk_buff只存一个报文,这也是TCP不需要设置报文大小的体现。
TCP可靠性的本质
正确理解可靠性:
1)具有应答,可以保证历史消息的可靠性!(100%保证)
2)通信中,最新的报文永远没有应答,最新报文可靠性无法保证
保证可靠性,TCP核心:确认应答机制(让报文不是最新的)
确认应答理解:例如客户端向服务端发送数据,服务端必须应答,只要客户端收到了服务端的应答,就可以100%保证数据被服务端收到了。
TCP的通信过程(32位序号,32位确认序号)
客户端向服务端发送数据,服务端应答,客户端收到应答,由此可以保证客户端到服务器的可靠性。相反则可以保证服务器到客户端的可靠性(且不对应答做应答,防止无穷递归)
TCP传递报文时,更通用的过程:发送一堆请求,等待服务器应答。
TCP结构:32位序号,32位确认序号
确认序号
- 数值:确认序号 = 序号 + 1
- 表达意义:指定报文需要之前的所有信息,已经全部收到(下一次发送,从确认序号开始)
序号作用:排序,解决乱序问题(发送顺序和接受顺序不一致,UDP解决不了)
为什么会要有两个序号?
例如:客户端向服务器发送数据,服务器可以进行捎带应答(应答+数据),此时服务器TCP报头的序号填自己独立的,TCP确认序号为client序号+1。因为有捎带应答的情况,此时必须要有两个序号进行区分。
流量控制(16位窗口大小)
16位窗口大小含义:当前连接接收缓冲区剩余大小。
**问题:以客户端向服务端发送数据为例,客户端向服务端发送了大量数据,服务端的接收缓冲区装满了数据。客户端再向服务端发的时候,服务端已经满了。这个数据就只能被丢弃,那么tcp怎么能保证可靠性?不影响,没有收到对应的应答,重传就完了。这个做法本身没有错误,也能实现,但是问题就是这样做太耗费资源了,**费尽千辛万苦送到缓冲区,结果被丢弃了,为什么服务端不早点说接收不了呢?
16位窗口大小:表示当前连接的接收缓冲区剩余大小。通过读取这个字段,客户端就可以知道服务端的接受能力了。
流量控制:主要解决的是效率问题,接收缓冲区剩余大小大的话,就发快一点,反之发慢一点,就可以尽可能减少资源的浪费。
标志位存在意义
标志位本质:报头中的比特位
为什么要有标志位?
要有表示报文类型的字段(例如不能对应答做应答,就要区分应答和数据)。接收方收到的TCP报文,一定会存在不同的类型,针对不同报文类型,接收放要有不同的做法。
6个TCP标志位
ACK:确认号是否有效(标明报文是一个应答报文)acknowledge
SYN:同步标志位,连接建立,握手过程使用的标志位(同步报文段)。FIN:通知对方,本端要关闭了(结束报文段)。
PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走(psh让系统调用read的条件就绪,比如read本来要500个字节才能解除阻塞,psh可以改。)关于三次握手: 前两次握手不能携带数据,因为第三次握手没有完成!
RST:对方要求重新建立连接(复位报文段)
例如:TCP通信,客户端向服务端发起连接,三次握手后,客户端在发出去请求,收到服务端的捎带应答后,发出应答,不管这个应答有没有丢失,它都认为自己已经建立连接了。对于服务端而言,要晚一步,服务端要收到这个应答才能表明建立连接了。如果在这个过程中,客户端发送的应答丢失了,客户端还是认为自己建立好了连接,给服务端发送数据,服务端接受到对应的数据就会发送设置好RST的TCP报头,要求客户端重新建立连接。
通信的过程中,连接出现任何问题,都可以进行重置!
URG:紧急指针是否有效
tcp是保证可靠性的 -> 序号 -> 按序到达的 -> 接收缓冲区 -> 字节流式的接收队列 -> 如果我们有数据,想被优先读取(非主流),优先处理(情况:取消下载,取消上传,需要插队,不要等下载完在取消,效率低)
紧急数据,并不属于常规数据 -> 带外数据。紧急数据,只有一个字节(可用作状态码)。
16位紧急指针含义:当前报文有效载荷中,特定偏移量处,有紧急数据(一个字节)
序列号初步理解
TCP将每个字节的数据都进行了编号,即为序列号。发送缓冲区看成一个字符数组,那么发送缓冲区,每一个字节,天然不就有编号了!
理解丢包,理解应答报文
发送方没有收到ACK,意味着什么?
意味数据包丢失了吗?-> 只能意味着:数据可能丢失,对方可能没收到(例如:数据没丢丢包,ACK丢包了)
意味着:要么数据丢,要么应答丢 -> 无法100%保证对方是否收到消息,无法保证可靠性。
如何判定是真的丢包了呢?
特定的时间间隔,收不到应答,判定报文丢失! -> 收不到应答 && 超时
如果数据包没丢失,服务端收到了,但由于应答丢了,客户端超时重传了,此时数据就重复了,怎么解决?
序号的作用:确认应答,按序到达,去重
时间间隔应该是多长?
最理想的情况下,找到一个最小的时间。TCP为了保证无论在任何环境都能比较高性能地通信,因此会动态计算这个最大超时时间。
超时以500ms为一个单位进行控制,重传每次叠加,如果超过一定重传次数,依旧收不到应答,那么TCP认为网络或者对端主机出现异常,强制关闭连接。
连接管理和连接状态
状态就是整数,定义的宏值。
连接会大量存在,有的刚建立,有的已经释放,要不要管理?要,先描述,在组织struct link。
connect会发起三次握手(cilent),accept不参与三次握手(server),握手工作由OS自动完成。
如何理解accept不参与三次握手?
客户端调用connect向服务端发起连接,不需要服务端进行accept也能完成,并成功与服务端连接。accept只是把OS中已经建立好的连接(struct link)拿上来用了。
为什么建立连接要三次握手?
两个理由:
1)以最小成本,100%确认双方通信意愿
2)以最短的方式,进行验证全双工(本质验证:我们两个所处网络是通畅的,能支持全双工)
如何理解:
2)以cs举例,客户端向服务端发送SYN报头,服务端收到并捎带应答发送SYN+ACK报头,
此时客户端收到了,客户端的发送(ACK报头)和接收(收到SYN+ACK报头)就好了,服务端的接收(SYN报头)也验证了,客户端发送ACK报头,服务器到,服务端的发送能力也验证了(验证了网络通畅和双方都支持全双工)
**1)**3次握手,是成本最低的,本质也是4次握手,只不过服务端捎带应答(SYN+ACK)
面对客户端的连接请求,服务器都要无脑接受(捎带应答)