如果我们把网络体系按照OSI模型划分,那么不管是从上往下,还是从下往上数,处于最中间的都是传输层。
也就是说传输层是一个承上启下的作用,传输层主要就做一件事 —— 建立端到端的连接。
什么是端到端?
比如我们的客户端到服务端就是端到端,客户端和服务端都有自己的ip地址。那么问题来了,客户端和服务端虽然都有自己的ip地址可以取得联系,但是客户端和服务端都有各自的应用进程,而且都可能需要进行TCP连接,比如我在电脑上同时用谷歌和火狐浏览器登录CSDN,很明显CSDN需要把内容分发给两个不同的应用进程,除了ip地址以外还需要什么参数才能保证内容不会错发给应用进程?
那就是端口号。我们在访问CSDN服务器的时候除了ip地址以外,浏览器会自动给我们添加上端口号443,因为走的是 https 协议,电脑中会给谷歌和火狐分配不同的端口号,这样进行连接就会像“管道”一样特定的进行传输,如果用不同的账号给同一篇博客点赞的话,不同的赞就会通过各自的管道给同一篇博客,而ip地址加上端口号就叫做套接字socket。套接字socket就是握手前的核心条件。
在使用TCP连接的时候需要进行三次握手,但是怎么样的握手才能判断出哪些请求或者哪些响应需要丢弃?这才是握手机制的核心。
三次握手
TCP报文里面有SYN,ACK和FIN等标识,如果设置1就是开启这些标识,如果设置0就是关闭这些标识。
首先在客户端发送TCP报文的时候会把SYN开启,SYN表示Synchronization,原意是同步的意思,客户端表示想和服务端进行数据的同步,在同步以后,也就是三次握手以后,客户端就可以和服务端互相发送信息,毕竟TCP是全双工的,所以可以互发信息。
只是把SYN开启是不够的,报文里面还有一个重要的字段Sequence序号。为什么还需要添加这个Sequence序号呢?因为应用程序可能连续发送多个序号给服务器,这样服务器就起码有依据可以判断哪些是累赘信息,而且这个Sequence序号是随机生成的,作为初始值来进行后续判断依据,这样就更加保证了通道的唯一性,如果没有初始序号会有一种情况:客户端同时发送多条信息给服务端,服务端不知道究竟需要干什么。所以这个初始序号非常重要。
当服务器收到了客户端发来的 SYN以后就需要做出响应了,这个时候服务器会在TCP报文中把SYN和ACK开启,ACK表示Acknowledgment,确认的意思,SYN和ACK合起来就是“确认同步”的意思了。服务器也会生成自己的序号,假设这里的序号为Y,但是还不够,还要加上确认号,这个确认号就根据对方的序号+1得到。这样客户端在收到号码后-1就知道是不是自己发送的TCP报文了。
最后客户端还需要进行确认,因为如果不确认的话,服务器不知道自己发送出去的“确认同步”是否被接收,于是必须再发送一次TCP报文来使连接正式建立。这个时候客户端会把ACK开启,这里的序号就用对方的确认号生成,并且在确认号上根据对方的序号+1。但是现在问题来了,如果每一次发过来的SYN,服务器都要记住其序号,并且新生成自己需要记住的序号,那服务器就需要挂起非常多资源,如果有黑客借此不断发送SYN又不进行下一步,就会导致服务器原地崩溃,也就是典型的DDOS攻击,因此服务器干脆不保存自己的序号,而是根据服务器的ip地址和端口号等私有信息进行算法的运算得到序号。
我们来梳理一下整个过程:我们可以看出这里的确认号都是根据对方的序号+1得到的
第一次确认号是根据客户端序号X再加1得到的
第二次确认号是根据服务端序号Y再加1得到的
而且就控制位来说也是具有唯一性的,第一次是SYN,第二次是SYN+ACK,第三次是ACK。两遍不仅可以根据序号和确认号,还可以根据控制位来区分进行到哪个步骤,丢弃一些不必要的报文。
握手之后下个步骤是什么呢?
握手之后就建立了连接这个时候客户端就可以发送HTTP请求了,然后服务器相应内容。
四次挥手
假设现在内容都交流完毕了,各自可能就会发起关闭连接的要求了,这个过程就是我们说的4次挥手
注意:客户端和服务端都能主动发起关闭请求
假设这里是客户端主动发起关闭要求,这个时候客户端会在报文中开启FIN和ACK两个控制位,FIN就是finish结束的意思,这里也就是确认要结束会话。
因为在发送HTTP请求和响应的时候序号和确认号被不断递增,因此这里就不用固定数字来表示序号和确认号了
此时一般服务端会先发送一个ACK来进行确认,然后就和前面的套路一样,自己的序号用对方的确认号,自己的确认号用对方的序号+1。
虽然发送了TCP报文,但此时客户端并未正式关闭通道,因为服务端那边可能还有需要发送的数据,等服务端发送完数据以后会再发送一个FIN+ACK来进行最后的确认,此时序号和确认号不需要改变,因为没有一来一回,只是多了一个控制位FIN来进行确认结束步骤而已。
最后客户端得到最终的结束确认以后会发送ACK来进行确认,此时自己的序号需要用对方的确认号,自己的确认号用对方的序号再加上1
其实中间的ACK和FIN+ACK两部就足以证明为什么要4次握手,因为可能还存在未发送完毕的数据。