输入网址后发生了什么&没有listen(accept)能建立连接吗&SYN洪水

键入网址到网页显示,期间发生了什么

一、HTTP

1、浏览器解析URL(协议+哪一个服务器+哪一个服务+文件路径)
2、http协议得到URL提供的信息后,构建http请求报文

二、DNS 域名系统

1、通过DNS,借助域名获得具体的IP地址
2、DNS有层级关系(根域名服务器→顶级域名服务器→权威域名服务器)。本地域名服务器向各级DNS服务器递归查询
3、本地域名服务器有DNS缓存,所以也不是每次都要查询,可能会有本地缓存。

三、TCP

1、HTTP是基于TCP的,所以会经过TCP协议。
2、TCP报头
在这里插入图片描述
3、TCP是面向字节流基于连接的可靠数据传输协议。所以通信双方需要先建立连接
4、TCP通信双方三次握手建立TCP连接
5、TCP在分割上层传下来的数据后(MSS),就可以添加上TCP报头,就组成了TCP数据报,继续向下层传输

四、IP

1、根据目的IP地址,查询自己的路由表,决定由哪一个网卡发出数据(目的IP & 子网掩码),若没有匹配的就发给默认网关。
2、在这里关键就是如何生成路由表。

五、MAC

1、源MAC地址是固化在网卡中,直接读取;目的MAC地址是需要通过ARP协议获得。
2、局域网内,利用局域网通信原理,广播查询信息,目的主机就会把MAC地址返回给源主机。也会有ARP缓存。
3、然后添加MAC协议报头后形成MAC帧

六、网卡

1、网卡+网卡驱动程序,将数字信号→电信号(需要加上一些控制信息)

七、交换机

1、现在数据已经离开源主机,进入网络中
2、交换机收到数据包后,解析获得目的MAC地址。然后再自己的MAC地址表中查询应该让数据包从交换机的哪一个端口放出。
3、如果MAC地址表中没有目的MAC地址怎么办?交换机会把数据包从所有端口中都放出去,只有数据包的主人才会接收数据包,其余人都会忽略,目的主机接收后就会给交换机返回相应包,这样交换机就知道该MAC地址在那个端口,就可以写入自己的MAC地址表。、
4、虽然交换机是二层设备,但是经过交换机不回改变数据包的目的MAC地址。

八、路由器

1、经过路由器,那么数据包就要离开子网,进入更大的网络了。
2、路由器的功能是转发+路由
3、路由器端口首先获取数据包,先查看MAC地址是否与自己的MAC一致,一致则去点原先的MAC头部(MAC 头部的作用就是将包送达路由器)
4、获取目的IP查询然后通过路由表查询。
(1)如果网关是一个IP地址,则这个IP地址就是我们要转发到的目的地址。
(2)如果网关为空,说明已经到达目的地。
5、然后查询相应MAC地址,修改目的MAC后转发至相应的端口。
6、发送出去的网络包会通过交换机到达下一个路由器。由于接收方 MAC 地址就是下一个路由器的地址,所以交换机会根据这一地址将包传输到下一个路由器。

sk_buff

网卡上有数据如何得知?

一开始采用的硬件中断,但是频繁的发生硬件中断十分影响效率。网络包分厂多,CPU一直执行中断处理程序,根本无法执行用户程序,就会造成”饥饿“问题。
所以采用了【NAPI机制】,一种 【中断+poll轮询】 的方式

NAPI:NAPI的核心概念就是不采用中断的方式读取数据,而是首先采用中断唤醒数据接收的服务程序,然后 poll 的方法来轮询数据。

网络中如何描述并组织网络数据包??

使用的就是sk_buff
每一个数据包用一个sk_buff描述
在这里插入图片描述
使用双向链表将每一个sk_buff连接起来,其中还有成员为 struct sock* sk,指向该数据包对应的套接字数据结构。
每一个sk_buff都有一个字段指向双向链表的【头】,【头】中包含了双向链表的基本信息。

发送网络数据的时候,出现了几次内存数据拷贝??

第一次,调用发送数据的系统调用的时候,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区。

第二次,在使用 TCP 传输协议的情况下,从传输层进入网络层的时候,每一个 sk_buff 都会被克隆一个新的副本出来。副本 sk_buff 会被送往网络层,等它发送完的时候就会释放掉,然后原始的 sk_buff 还保留在传输层,目的是为了实现 TCP 的可靠传输,等收到这个数据包的 ACK 时,才会释放原始的 sk_buff 。

第三次,当 IP 层发现 sk_buff 大于 MTU 时才需要进行。会再申请额外的 sk_buff,并将原来的 sk_buff 拷贝为多个小的 sk_buff。

没有listen,能够建立TCP连接吗??????

先复习一下三次握手的过程

在这里插入图片描述

半连接队列 & 全连接队列

1、半连接队列(SYN队列):当服务器收到客户端的第一次握手请求(SYN请求)时,就会将该连接请求加入到半连接队列中,其中的套接字都是处于SYN_RECV状态
2、全连接队列(Accept队列):当服务器收到客户端的第三次握手请求(ACK请求【如果带有数据也会有SYN标志位】)时,通信双方正式建立TCP连接,会找到半连接队列中对应的套接字,其状态变为ESTABLISH状态,并加入全连接队列。

※半连接队列本质是一个哈希表,方便以O(1)的时间复杂度找到相应的套接字【套接字不可重复,可以作为天然的key值】
※全连接队列本质时一个链表,能够以O(1)的时间复杂度添加、能以O(1)的时间复杂度被accept( )取走【取头上的节点就可以】

全连接队列满了怎么办?

1、tcp_abort_on_overflow
我们在自己的Linux系统下查看这个变量发现是0
在这里插入图片描述
(1)当tcp_abort_on_overflow值为0时,全连接队列满了,就会丢弃第三次握手请求,并且开启定时器,重传第二次握手请求【ACK+SYN】,如果重传超过一定次数,就会把该半连接从半连接队列中删除
(2)如果值为1,全连接队列满了之后,就直接发RST给客户端,效果上看就是连接断了。

当服务端端口未监听时,客户端尝试去连接,服务端也会回一个RST。
这个和tcp_abort_on_overflow值为1的情况相同。所以客户端无法区分这两种情况。【全连接队列满了 or 服务器端口未监听】

半连接队列满了怎么办??【SYN洪水】

首先我们应该清楚,tcp连接处于半连接状态的时间应该是非常短的。因为半连接状态是处于第一次握手和第二次握手之间的时候。所以半连接队列如果满了,很有可能是遇到了SYN洪水【SYN Flood】

SYN洪水【SYN Flood】
客户端恶意向服务器疯狂发送第一次握手请求,服务器不知所以,傻乎乎都每次都给对方恢复第二次握手请求,可以客户端这个老渣男,不给你发送第三次握手请求,不跟你建立tcp连接,就导致服务器傻等,半连接一直处于满的状态,无法接收其他的连接请求,导致服务器无法正常提供服务。

我这里讲一个故事帮助理解(逗逗趣)
有一个特别漂亮的姑娘,十分腼腆,属于傻白甜类型。不会主动跟人沟通,但是又 渴望纯真的爱情。
有一天有一个“渣男”过来找他,跟姑娘承诺说:”我喜欢你,咱俩好吧!但是我现在要出去一趟,我回来咱们就结婚!你一定要等我呦~“ 傻白甜的姑娘就相信了他的承诺,就苦苦等待着渣男回来跟他在一起。同时渣男也一直写信告诉姑娘,我爱你,我会回来跟你结婚的~~~~
姑娘对此深信不疑,当有别的好男人过来追求她的时候,姑娘因为心里装满了渣男,也无法容下别人。但是渣男只是觉得姑娘傻,所以一直在骗她,姑娘就一直被蒙在鼓里,一直等待。直到姑娘的父母发现了这件事,于是终止了这门虚假的亲事【程序员发现的】

咳咳,跑偏了,博主写嗨了,让咱们回归正题哈~
我瞅瞅刚刚讲到哪里了。
刚刚介绍完SYN洪水攻击,相信大家应该都理解了。那我们应该如何解决呢?
方案就是syn_cookies 【1表示开启;0表示关闭】
在这里插入图片描述
开启syncookies后,当客户端发来第一次握手请求时,服务端并不会直接将其放入半连接队列中,而是直接生成一个cookies,这个cookies会跟着第二次握手,发回客户端。客户端在发第三次握手的时候带上这个cookies,服务器验证到它就是当初发出去的那个cookies,就会建立连接并放入到全连接队列中。可以看出整个过程不再需要半连接队列的参与。

这次这个傻白甜姑娘精明了,给了渣男一个定情信物,说:”你以后来娶我,就用这个证明你的身份就好,我就不用一直在心里记着你了“ 【姑娘的话外之意就是,我心里可以装别的男生了,服务器可以接收别的连接请求了】
但是!!!!还有一个问题,姑娘现在确实不需要在心里装男生了,但是她是不是要记住这个信物呢??信物可能在心中占用的空间少,但只要是占空间,很多很多个信物姑娘都要记在心中,是不是迟早会满?!!
所以姑娘想了个办法,我不要记住具体的东西,我自己设定一个公式,信物只要满足这个公式就证明你的身份了,这样我不用记,只需要拿到计算一下就好了!

正经的说 :服务器不需要半连接队列存储半连接了,但是使用syn_cookies,是不是还是要存储cookies呢?这样也迟早会耗尽我存储cookies的空间,所以,cookies服务器也不要存储了,通过现有的数据,设定一套公式,符合该公式的就是发送过第一次握手请求的连接,就可以establish了。

公式:其实计算cookies,就是借助双方的IP、端口、seq序号、时间戳等信息计算出来的【编码】,然后被服务器存储在tcp报头中发送给客户端。当客户端第三次握手时候,带上这个cookies,服务器【解码】,就可以获得IP、端口等信息,验证一下是否符合,就可以了。

那这么说,我们就可以完全放弃半连接队列这个方案了。全部使用syn_cookies即可
其实这也是错误的
(1)因为服务器不存储连接信息了,所以当传输过程中数据丢失了,服务器也不会主动重发第二次握手请求
(2)编码解码还是需要消耗CPU资源的,如果攻击者疯狂伪造cookies(简单来说是随机值)但是服务器还是不停的解码计算,发现是虚假的请求才丢弃,是不是对CPU的纯纯浪费,严重就会导致正确的第三次握手请求到来时,CPU忙的不可开交,都没时间去处理这个正确的请求了。

没有listen,为什么还能建立连接?

我们知道执行 listen 方法时,会创建半连接队列和全连接队列。三次握手的过程中会在这两个队列中暂存连接信息。所以形成连接,前提是你得有个地方存放着,方便握手的时候能根据 IP + 端口等信息找到对应的 socket。
对于客户端,因为并没有执行listen,所以不会存在半连接、全连接队列。
但是!内核中存在一张全局哈希表,用于存放sock的连接信息

(1)在 TCP 自连接的情况中,客户端在 connect 方法时,最后会将自己的连接信息放入到这个全局 hash 表中,然后将信息发出,消息在经过回环地址重新回到 TCP 传输层的时候,就会根据 IP + 端口信息,再一次从这个全局 hash 中取出信息。于是握手包一来一回,最后成功建立连接。
(2)客户端之间的连接也是同理。

没有accept呢?
这个问题更加好解释了,服务器在accept之前,其实已经和客户端建立连接了,连接就在全连接队列中,accept只不过是将ESTABLISHED状态的连接拿上来处理而已。

总结
其实不需要严格按照常规的方式使C/S之间建立连接。而且客户端与服务器也只是相对来说,不是绝对的,谁能提供服务谁就是服务器。
其次,只要能够找到套接字信息就可以建立连接(存储着自己的套接字信息即可)

服务器没有listen,客户端直接发送数据

服务端如果只 bind 了 IP 地址和端口,而没有调用 listen 的话,然后客户端对服务端发起了连接建立,服务端会回 RST 报文。

总结于小林coding-图解网络

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值