握手前
如果我们把网络体系按照OSI模型划分,
第几层 | 网络体系 |
---|
1 | 物理层 | 2 | 数据链路层 | 3 | 网络层 | 4 | 传输层 | 5 | 会话层 | 6 | 表示层 | 7 | 应用层 |
不管是从上往下还是从下往上,处于最中间的都是传输层,也就是说传输层起到一个承上启下的作用,传输层主要就干一件事,建立端到端的连接,比如我们的客户端到服务器端就是端到端。
核心条件 socket
用一个栗子来说明什么是socket :
有一个飞机,飞机起飞了,我们知道每辆飞机都是会被管制中心监管的,这样就会实时获取每辆飞机的位置,这其实和我们TCP进行连接之前是一样的条件,客户端和服务端都是有各自的IP地址,正如飞机有自己的实时飞行地址,管制中心有自己的固定地址,飞机上如果有什么情况就可以直接和管制中心联系,可以是第一机长或者是第二机长进行联系,管制中心会有特定的人员对这辆飞机进行答复;现在问题来了,客户端和服务器端虽然都有IP地址可以取得联系,但是客户端和服务器端都有各自的应用进程,而且都可能需要进行TCP连接,比如我在电脑里同时用谷歌和火狐浏览器登录B站,那很明显B站需要把内容分发给两个不同的应用进程,除了IP地址以外还需要什么参数才能保证内容不会错发给应用进程呢,那就是端口号了,我们在访问B站服务器的时候除了IP地址以外,浏览器会自动给我们添加上端口号443,因为走的是HTTPS协议,电脑中会给谷歌和火狐分配不同的端口号,这样进行连接就会像“管道”一样特定的进行传输,如果用不同的账号给同一个UP主投币的话,不同的硬币就会通过各自的管道投给同一个UP主,IP地址加上端口号就叫做套接字socket
socket 通常称为套接字(握手前的核心条件),用于描述IP地址和端口号 IP地址:端口号 ,是一个通信链的句柄。应用程序通过socket套接字向网络发起请求或应答网络请求的。
客户端和服务器通过socket进行交互,服务器需要绑定在本机的某个端口号上,客户端需要声明自己连接哪个地址的哪个端口,这样服务器和客户端就能连接了。
三次握手
现在飞机遇到故障了,机长马上联系管制中心的Bob,
机长:“成都成都,四川8633” ; Bob:“四川8633,请讲”; 机长:“我现在有点故障,我申请下高度”;
这里其实就是三次握手了,因为飞机在飞行过程中需要调整高度或者是返航等操作需要得到同意才能执行,如果在没得到同意飞机直接调整高度,那么很可能会和其他飞机发生撞击事件;其实计算机的世界里也是这样,在使用TCP连接的时候就需要进行三次握手,但是怎么样的握手才能判断出哪些请求或者哪些响应需要丢弃,这才是握手机制的核心。TCP报文里面有SYN 、ACK 和FIN 等标识,如果设置1就是开启这些标识,如果设置0就是关闭这些标识;首先在客户端发送TCP报文的时候,会把SYN 开启,SYN 表示Synchronization,原意是同步的意思,客户端表示想和服务端进行数据的同步,在同步以后,也就是三次握手以后,客户端就可以和服务端互相发送信息,毕竟TCP是全双工的,所以可以互发信息,但是只是把SYN 开启是不够的,报文里面还有个重要的字段Sequence序号 ,为什么还需要添加这个Sequence序号呢?因为应用程序可能连续发送多个序号给服务器,这样服务器就起码有依据可以判断哪些是累赘信息,而且这个Sequence序号是随机生成的,作为初始值来进行后续判断依据,这样就更加保证了通道的唯一性;如果没有初始序号会有一种情况
机长:“成都成都,四川8633” A:“今天晚上吃啥呀”
这个时候管制中心的Bob就很疑惑了,一下子发两条信息给我,究竟机长想让我干嘛?所以这个初始序号非常重要,假设这里的初始序号为8633,当服务器收到SYN 以后,就需要做出响应了,这个时候服务器会在TCP报文中把SYN 和ACK 开启,ACK 表示Acknowledgment,是确认的意思,SYN 和ACK 合起来就是 “确认同步” 的意思了,那么服务器也生成自己的序列号,假设这里的序号为303,但是还不够,还要加上确认号,这个确认号就根据对方的序号+1得到的,这样客户端在收到号码后-1就知道是不是自己发送的TCP报文了,最后客户端还需要进行确认,因为如果不确认的话,服务器不知道自己发送出去的“确认同步”是否被接收,于是必须再发送一次TCP报文来使连接正式建立,这个时候客户端会把ACK 开启,这里的序号就用对方的确认号生成,并且在确认号上根据对方的序号+1,但是现在问题来了(下面有详解):如果每一次发过来的SYN,服务器都要记住其序号,并且新生成自己需要记住的序号,那服务器就要挂起非常多的资源,如果有黑客借此不断发送SYN又不进行下一步,就会导致服务器原地崩溃,也就是典型的DDos攻击(SYN洪泛攻击),因此服务器干脆就不保存自己的序号,而是根据服务器的IP地址和端口号等私有信息进行算法的运算得到序号;梳理一下过程,可以看出这里的确认号都是根据对方的序号+1得到的,第一次确认好是根据客户端序号8633再加1得到的,第二次确认号是根据服务端序号303再加1得到的,而且就控制位来说也是具有唯一性的,第一次是SYN ,第二次是SYN+ACK ,第三次是ACK ,两边不仅可以根据序号和确认号,还可以根据控制位来区分进行到哪个步骤,丢弃一些不必要的报文。
什么是半连接队列?
服务器第一次收到客户端的SYN 之后,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。 当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中;如果队列满了就有可能会出现丢包现象。
试想如果是用两次握手,则会出现下面这种情况:
如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源。
三次握手过程中可以携带数据吗?
其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据。 为什么这样呢? 可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。 也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。 而对于第三次的话,此时客户端已经处于已建立连接状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
SYN攻击是什么?
如果每一次发过来的SYN,服务器都要记住其序号,并且新生成自己需要记住的序号,那服务器就要挂起非常多的资源,如果有黑客借此不断发送SYN又不进行下一步,就会导致服务器原地崩溃,也就是典型的DDos攻击(SYN洪泛攻击),因此服务器干脆就不保存自己的序号,而是根据服务器的IP地址和端口号等私有信息进行算法的运算得到序号
检测 SYN 攻击非常的方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。
常见的防御 SYN 攻击的方法有如下几种: 缩短超时(SYN Timeout)时间 增加最大半连接数 过滤网关防护 SYN cookies技术
三次握手的作用是为了确认双方的接收与发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。
握手后
握手后就建立了连接,这个时候客户端就可以发送HTTP请求了,然后服务器响应内容
四次挥手
假设现在内容都交流完毕了,各自可能就会发起关闭连接的要求了,这个过程就是我们说的四次挥手了。
【注意】:客户端和服务端都能主动发起关闭请求;
假设这里是客户端主动发起关闭要求,这个时候客户端会在报文中开启FIN 和ACK 两个控制位,FIN 就是Finish结束的意思,这里也就是确认要结束会话。此过程发送HTTP请求和响应的时候序号和确认号是不断递增的;此时一般服务端会先发送一个ACK 来进行确认,然后和前面一样,自己的序号用对方的确认号,自己的确认号用对方的序号再加上1,虽然发送了TCP报文,但此时客户端并未正式关闭通道,因为服务端那边可能还有需要发送的数据,等服务端发送完数据以后会再发送一个FIN+ACK 来进行最后的确认,此时序号和确认号不需要改变,因为没有一来一回,只是多了一个控制位FIN来进行确认结束步骤而已,最后客户端得到最终的结束确认以后会发送ACK 来进行确认,此时自己的序号需要用对方的确认号,自己的确认号是对方的序号+1,其实中间的ACK 和FIN+ACK 两步就足以证明为什么要4次挥手,因为可能存在未发送完毕的数据。
|