计算机网络核心知识--1.2 TCP的三次握手

TCP协议与IP协议:
IP协议是无连接的通信协议,它不会占用两个正在通信的计算机之间的通信线路,这样,IP就降低了对网络线路的需求,每条线可以同时满足许多不同计算机之间的通信需要,通过IP,消息或者其他数据会被分割成较小的独立的包,并通过因特网在计算机之间传送,IP负责将每个包路由至它的目的地,但IP协议没有做任何事情来确认数据包是否按顺序发送,或者包是否被破坏,所以IP数据包是不可靠的,需要由它的上层协议来做出控制。
前面我们学到传输控制协议TCP是属于传输层的协议。

传输控制协议TCP简介:
(1)面向连接的,可靠的,基于字节流的传输层通信协议;
(2)将应用层的数据流分割成报文段并发送给目标节点的TCP层;
(3)数据包都有序号,对方收到则发送ACK确认,未收到则重传;
(4)使用校验和来检验数据在传输过程中是否有误。

TCP协议是面向连接的,可靠的,基于字节流的传输层通信协议,数据传输时,应用层向TCP发送数据流,然后TCP把数据流分割成适当长度的报文段,报文段的长度通常受计算机连接的网络的数据链路层的最大传输单元,即MTU的限制,之后TCP把结果包传给IP层,由它来通过网络将包传给目标节点的TCP层,TCP为了保证不丢失包,就给每个包一个序号,即sequence number,同时序号也保证了传送到目标节点的包的按序处理,然后接收端实体对已成功收到的包发回一个相应的确认,即ACK确认。如果发送端实体在合理的往返时延,即RTT内,未收到确认,那么对应的数据包就会被假设已丢失,并且将会对其进行重传。TCP用一个奇偶校验和函数来检验数据是否有错误,在发送和接受时都要计算校验和。

TCP报文的头部:
在这里插入图片描述
如图所示,源端口和目的端口分别占两个字节,TCP和后面要学习的UDP的数据包,都是不包含IP地址信息的,因为那是IP层上的事,但是TCP和UDP都会有源端口和目的端口,就是说端口这东西是属于传输层知识范畴的。我们知道两个进程在计算机内部进行通信,可以有管道,内存共享,信号量,消息队列等方法进行通信的,而两个进程如果要进行通信,最基本的前提是能够唯一标识一个进程,通过这个唯一标识找到对应的进程。在本地进程通信中,我们可以使用pid,即进程标识符,来唯一标识一个进程,但pid只在本地唯一,如果把两个进程放在不同的两台机,然后它们要进行通信的话,pid就不够用了,这样就需要另外一个手段了。解决这个问题的方法,就是要在传输层中使用协议端口号,我们知道IP层的IP地址可以唯一标识主机,而TCP协议和端口号可以唯一标识主机中的进程,这样我们可以利用IP地址加协议加端口号这么一个唯一标识,去标识网络中的一个进程。在一些场合,也把这种唯一标识的模式,称为套接字,即socket,也就是说,通信的重点是应用进程,但我们只要把要传送的报文交到目的主机的某一个合适的端口,剩下的工作就由TCP来完成了。

下面看sequence number,即序号,占4个字节,TCP连接中传送的字节流中的每一个字节,都按顺序去编号的,例如,一段报文的的序号字段值就是107,而携带的数据共有100个字段,那么如果有下一个报文段的话,其序号就应该是从107+100=207开始。

下面看Acknowledgment Number,即确认号,占4个字节,是期望收到对方下一个报文的第一个数据字节的序号,例如B收到A发送过来的报文,其序列号字段为301,而数据长度是200字节,这表明了B正确的收到A发送的到序号500为止的数据,即301+200-1就是500为止的数据,因此,B期望收到A的下一个数据序号是501,于是,B在发送给A的确认报文段中,会把ACK确认号置为501。

接下来是offset,即数据偏移,由于头部有可选字段,长度不固定,因此它指出TCP报文的数据距离TCP报文的起始处有多远。

接下来就是reserved,即保留域,保留今后使用的,但目前都会被标为0。

接下来就是TCP FlagS,即控制类,主要由8个标志位来组成,每一个标志位表示一个控制功能,这里主要介绍常见的6个:
(1)URG:紧急指针标志,当它为1时,表示紧急指针有效,为0则忽略紧急指针。
(2)ACK:确认序号标志,为1表示确认号有效,为0表示报文中不含确认信息,可以忽略确认号字段。
(3)PSH:push标志,为1表示是收到带有push标志的数据,指示接收方在接到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。
(4)RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现的错误连接,或者用于拒绝非法的报文段和拒绝连接请求。
(5)SYN,同步序号,用于建立连接过程。在连接请求中,SYN等于1和ACK=0表示该数据段没有使用捎带的确认域,而连接应答捎带一个确认,即SYN=1和ACK=1。
(6)FIN,即finish标志,用于释放连接。为1时表示发送方已经没有数据发送了,即关闭本方数据流。

接下来是Window窗口,指的是滑动窗口的大小,用来告知发送端,接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。

然后是checksum,即检验和,指的是奇偶校验,此校验和是对整个TCP报文段,包括TCP头部和TCP数据,以16位进行计算所得,由发送端进行计算和存储,并由接收端进行验证。

接下来是urgent pointer,即紧急指针,只有当TCP flags中的URG为1的时候才有效,指出本报文段中的紧急数据的字节数。

最后一项是TCP options,即可选项,其长度可变,定义一些其他的可选参数。

当应用程序希望通过TCP与另一个应用程序通信的时,它会发送一个通信请求,这个请求必须被送到一个确切的地址,在双方握手之后,TCP将在两个应用之间建立一个全双工的通信,这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止,而上面所说的握手,即上面所说的TCP的三次握手的流程图如下:
在这里插入图片描述
在这里插入图片描述
为什么需要三次握手才能将连接建立起来?
主要是为了初始化sequence number的初始值。通信双方要互相通知对方自己的初始化的sequence number,也就是上图中的x和y,这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输问题而乱序,即TCP会用这个序号来拼接数据,因此在服务器回发它的sequence number,即第二次握手之后,还需要发送确认报文给服务器,告知服务器说,客户端已经收到你的初始化sequence number了。此外,在第一次握手的时候,有一个隐患,即syn得超时问题,如果server端接收到client端发的syn之后,回复了syn-ack,回了之后client就掉线了,此时server端没有收到client发送回来的ack的确认,那么这个连接就会处于一个中间状态,即没有成功,也没有失败。于是server端如果在一定时间内没有收到客户端的回执,那么它就会重发syn-ack。在Linux下默认重试次数为5次,重试的间隔时间从1s开始,每次都翻倍,5次的重试时间间隔为1s,2s,4s,8s,16s,总共就是31秒,在第五次发出去之后,还需要等待32s才能被判定为超时,所有我们总共需要1+2+4+8+16+32=63s的时候,TCP才会断开这个连接,那这样子会造成什么样的后果呢?那就是可能会使得服务器遭到syn flood的攻击的风险,恶意程序会给服务器发送一个syn报文,发了之后它就下线了,于是服务器需要默认等63s才会断开这个连接,这样攻击者就可以把服务器的syn连接的队列耗尽,让正常的连接请求不能处理。于是Linux下就给了一个tcp_syncookies的参数来应对这个事,当syn队列满了以后,TCP会通过源地址端口,目标地址端口和时间戳,打造一个特别的sequence number回发回去,这个sequence number简称syncookies,如果是攻击者是不会有响应的,如果是正常连接,则会把这个syncookies发回来,然后服务端可以通过cookie建立连接,通过syn cookie,即便此时syn队列满了,本次连接请求不在队列中,依然能够建立连接,进而解决了该问题的发生。

如果已经建立了连接,而client出现故障怎么办?
其实TCP还设立有保活机制。在一段时间,我们称为保活时间,即keepalive-time,在这段时间内,连接处于非活动状态,开启保活功能的一端,将向对方发送一个保活探测报文,如果发送端没有收到响应报文,那么经过一个已经提前配置好的保活时间间隔,将继续发送保活探测报文,直到发送探测报文的次数达到保活探测数,这时对方主机将会被确认为不可达,连接也将被中断。

猜你喜欢

转载自blog.csdn.net/tanwenfang/article/details/89097174