源目的端口号:整个tcp头一共20个字节,前四个字节分别是源端口号和目的端口号,这是最基本的用于传输层的分解与复用
序列号:在连接建立是初始化一个随机数,然后每次递增上个tcp包发送的字节数,用来保持报文段的有序
确认应答号:标识接收方下一次希望收到报文段的序列号,同时表示这个序列号之前的包已被正确接受,用来解决丢包的问题
控制位:都是只有一个bit
窗口大小:是用于流量控制的
很简单就八个字节,两个端口号就占了四个字节,外加一个字节记录包的长度,一个字节的校验和。包长度是指包括头和数据的整个包的长度,校验和用于整个包的受损检验
TCP与UDP的区别:
这个没什么好说的,不允许丢包,不允许失序,保证数据可靠就用TCP,但是像是游戏,音视频通话,音视频服务可以允许丢包但是要求强实时性的就能用UDP,额外需要注意的就是DNS使用UDP协议
为什么TCP有首部长度字段,但是UDP没有
这个很好解释,因为TCP头部有可选的字段,可变长
为什么TCP头部没有包长度字段
可以根据IP包来算,IP包长度减去IP首部长度再减去TCP首部长度
那按照这么说,UDP数据包长度不也多余了??
观察上图,发现:除开close状态,其他转台转换都是在接收到对方的包之后发生的
对于客户端:
对于服务器:
上图的三个包也是就建立连接的三次握手,第一个SYN向服务端表示客户端的发送能力,第二个SYN表示客户端的接受能力和发送能力,因为是对第一个SYN的响应,第三次客户端正式开始发送数据,表示自己的接受能力,因为这次发送实际上也是对第二次SYN的响应
首先,显而易见的就是上一个段落所说的证明双方接受发送数据的能力,
其次:
为什么有IP分段机制了,还要在传输层进行TCP分段,因为TCP有重传机制,要是在IP分段,一个IP包的丢失造成一个TCP包的不完整,这个时候要重传没有分段的整个TCP包,开销就大了。相较于在传输层分段,TCP包比较小。
第一次握手丢失,和服务器是完全没关系的,因为服务器压根就不知道这回事,超时之后客户端就会重传,并且这个TCP的包和之前的应该是完全一样的。
讲一下细节,最大重传数和超时时间应该都是由内核参数决定的,最多五次,第一次1秒,第二次2秒,第三次3秒,依次类推。当最大重传次数次尝试都没有响应后,客户端断开连接
第二次握手丢失,对于客户端来说,现象也跟第一次握手丢失一样,客户端根本不知道丢失的是哪一次,只知道自己的第一次握手没有响应,因此客户端会像上一段落所说的一样重传
对于服务器呢?第二次报文丢失意味着接收不到第三次握手,服务器以为第二次报文丢失了,当然也可能是第三次报文丢失了,对于服务器来说都一样,他会重传第二次报文
重传次数有另外一个内核参数决定,所有尝试失败之后,断开连接
类比上一个段落,这次服务器还是会以为第二次报文可能丢失了,重传第二次报文。
但是第三次握手的报文不会由客户端重传。
粗略的根据名字猜一下,半链接队列就是服务器收到第一次握手之后维护状态的队列,全连接队列就是在服务器收到第三次握手之后维护状态的队列。
实际如下
全连接队列中的数据才会交给用户态中的程序
如果有人伪造虚假的SYN报,IP报中的源ip和端口都是随便造的,那么服务器响应的第二次握手便会石沉大海,得不到响应。半连接队列只进不出,最后空间被占满,多余的请求被丢弃。
挥手过程就是客户端和服务段互相挥手以及响应,为什么服务端对客户端的响应和服务端的挥手不能和为一次呢?因为从服务端被通知要断开连接之后,还有很多数据要处理和发送。这些数据处理和发送完了之后,服务器发送一个FIN,表示自己不会发送信息了,你也不必再听了。
主动发起断开连接的一方才会有最后的TIME_WAIT,这段等待时间是等等对方会不会重传FIN,这就意味着响应的ACK可能丢失了
发生了什么服务器不知道,但是没有服务器的ACK,客户端就会一直重复FIN,重复几次后客户端不管三七二十一了,直接关闭连接。(最大重传次数是由某个内核参数决定的)
ACK是不会重传的,对于服务器来说,这次挥手丢失了他也不管,他也不会知道,但是客户端没有ACK,就会以为是自己第一次挥手丢了,于是重传第一次挥手,重复几次没结果之后就直接关闭连接
主动断开连接的一方在收到第一次挥手的响应之后,进入FIN_WAIT_2状态,如果主动断开连接的一方是调用的close()函数,那么这个状态就只会持续一段时间,如果是shutdown()函数,那么他就会一直死等对方的FIN。
对于被动方,由于接收不到最后一次ACK,他就会一直重传第三次挥手,超过最大次数之后,直接断开连接。
重复一遍,ACK包是不会被主动重传的,被动方还是会觉得自己的包丢失了,重传第三次,重复多次没效果直接关闭。
对于主动方,发完最后的ACK后会等待一段时间,这样做的目的是观察还有后续的被动方的FIN的重传没有,如果有,就意味着自己最后一次ACK丢失了,那就得重传。
等待的这段时间被称为2MSL,即两倍报文最大生存时间,为什么是两倍最大生存时间,假使最后一个ACK丢失了,被动方没收到,有重传了一次FIN,那么从主动方最后一次ACK -> ACK丢失 -> 被动方重传FIN到达主动方 这个过程所花费的时间应该不会超过两倍报文最大生存时间。
当然,每当主动方收到新的FIN,这个时间都会被重置成2MSL,因为每次都可能丢。
防止错误的四元组数据被错误的接受:想象一下两个主机中的两个端口号在短时间内重复两次建立连接拆除连接,那么第一次连接中的被延误的包有没有可能在第一次连接已经被拆除之后又被第二次连接错误的识别?
这是完全有可能的,传输层数据包到达目的端口号后,最后的校验也就是看期望的序列号和包实际的序列号匹不匹配了,初始序列号是如何产生的?实际上是基于四元组和时间的,两次连接的四原则相同,时间又很接近,那就很容易误解。
保证被动方能正确关闭连接
这个很好理解,在第四次挥手丢失里面说过
但是如果没有等待时间,难不成服务器关闭不了了?那也不是,主动方发了ack就直接关了,假如ACK丢失了,服务器会重传FIN到主动方,这个时候主动方就不能理解这个FIN是什么意思了,因为原来的连接都没了,那么就会穿一个reset表示错误,这个时候被东方也就会关闭连接,但是这样总不优雅。
主要是两方面危害
占用系统资源,CPU 内存啥的,你得维护一个本可以结束的连接,肯定得要资源。
占用端口。
这种情况只会在一种情况下出现,那就是同一个端口对同一个端口,也就是上个段落中讲到的情况,因为一个链接是由一个四元组标定的,要是客户机短时间内在同一个端口上对服务器的同一个端口发起两次连接,就会出现占用