Netty 基础—基础网络协议
1.网络基础的相关问题总结
首先是七层模型和四层模型,然后是一次请求的全过程,接着是传输层的 TCP 连接(三次握手和四次挥手),然后就是传输层 TCP 协议上的 Socket 编程,最后是应用层的 HTTP 协议。
2.七层模型和四层模型
TCP/IP 四层模型(应传网数):应用层、传输层、网络层、数据链路层。其中每一层对应的协议分别是:数据链路层(以太网协议),网络层(IP 协议),传输层(TCP 协议),应用层(HTTP 协议)。
OSI 七层模型(应表会传网数物):应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。注意:物理层(网线和光缆传递 01 电信号),会话层、表示层、应用层 -> 应用层。
电脑的网络设置中一般会包含 IP 地址、子网掩码、网关地址、DNS 地址。IP 地址和子网掩码用来划分子网,判断哪些 IP 地址在一个子网内。IP 地址和 mac 地址是关联起来的,可以唯一定位网卡。网关地址可以认为是路由器上的那个网卡的 IP 地址,路由器上的网卡也有 mac 地址,mac 地址对应了一个 IP 地址。
3.物理层(网线 + 光缆 + 01 电信号)
物理层指的是通过网线或光缆把各个电脑连接起来形成一个网络。物理层负责传输 0 和 1 的电信号,计算机的最底层就是 0 和 1 的电信号,所以物理层通过传输 0 和 1 的电信号把各个电脑连接起来。
4.数据链路层(以太网协议 + 网卡 mac 地址)
数据链路层负责处理 0 和 1 的电信号如何分组,比如在一连串的电信号中区分哪些 0 和 1 分为一组、对应什么意思。一组电信号是一个数据包,也叫一个帧(frame)。每个帧分成两个部分,标头(head)和数据(data)。标头是一些说明性的数据,比如发送者、接收者和数据类型等。
以太网协议规定了电信号的分组方式,在其协议下发送的数据包必须指定目标电脑网卡的 mac 地址。以太网协议规定了接入网络的所有设备都要有个网卡,每个网卡必须包含一个 mac 地址,mac 地址就是网卡的唯一标识。所以以太网协议里的数据包,或者说在数据链路层传输的数据包,必须从一台电脑的网卡传输到另外一台电脑的网卡。
在以太网里面,如果一台电脑发送一个数据包出去,会广播给局域网(子网)内的所有电脑的网卡,然后每台电脑都从数据包里获取接收者的 mac 地址,跟自己的 mac 地址进行对比,如果一样就说明这是发给自己的数据包。
5.网络层(IP 协议 + 子网划分 + 路由器)
(1)IP 协议区分电脑属于哪个子网
子网(局域网)内的电脑在以太网协议下,是通过广播方式将一个数据包发送给另一台电脑的。
但是如何知道哪些电脑是在同一个子网内的呢?网络层里有 IP 协议,通过 IP 协议就可以区分哪些电脑是一个子网的。
(2)两个 IP 地址是否属于一个子网
每台计算机都会分配一个 IP 地址,如 IPV4 版本的 IP 地址由 32 个二进制数字组成。其中前 24 位代表了网络,后 8 位代表了主机。
如果要判断两个 IP 地址是否是一个子网的,则要分别把两个 IP 地址和自己的子网掩码进行二进制的位与运算,位与运算后再比较一下代表网络的那部分,如果网络的那部分是一样的才是一个子网。
(3)两台电脑如何传输数据包
两台在子网内的电脑可以通过广播 + mac 地址的判断来发传输据包,两台不在子网内的电脑则不能通过广播而需要通过路由器来传输数据包。
(4)路由器的作用
路由器负责将多个子网(局域网)进行连接。家里的网络是一个子网,要访问的网站是另一个子网。
路由器其实就是配置了多个网卡的一个专用设备,可以通过不同的网卡接入不同的子网。
路由器上的每个网卡都有 mac 地址和对应的 IP 地址。路由器虽然有 mac 地址,但是不能通过 mac 地址寻址。必须通过 IP 地址寻址,所以路由器是工作在网络层的设备。
(5)交换机和路由器的对比
交换机主要用在局域网(子网)的通信,局域网里的电脑就是通过交换机把数据包广播到局域网内的其他电脑上。
交换机是通过 mac 地址来寻址和传输数据包的,基于以太网协议工作在数据链路层。路由器是通过 IP 地址来寻址和传输数据包的,基于 IP 协议工作在网络层。网关也是路由器的一种,工作在网络层。
LAN 就是 Local Area Network(局域网),WAN 就是 Wide Area Network(广域网),WLAN 就是 Wireless Local Area Network(无线局域网 WIFI)。
(6)两个局域网如何通过路由器通信
步骤一:首先路由器 1 配置了两块网卡分别和两个局域网相连。
步骤二:然后子网 1 的电脑先通过交换机将数据包发送给路由器 1。该过程要将路由器 1 的一块网卡的 IP 地址所对应的 mac 地址写到数据包头部,然后才能通过交换机将数据包广播出去到路由器 1。
步骤三:接着路由器 1 通过 IP 地址寻址后把数据包传输到路由器 2。
步骤四:当路由器 2 接收到数据包后会比较自己网卡里的 mac 地址确认是否给自己,然后会在子网 2 内将目标机器的 IP 地址对应的 mac 地址写入数据包头部,接着再次通过交换机广播给子网 2 的目标电脑。
一个局域网内的每台机器都有自己的 ARP 缓存,ARP 的作用是用来在一个局域网内让各个设备知道彼此的 IP 地址和 mac 地址的。
一个 IP 地址对应着一个 mac 地址。如果子网内的机器需要进行通信,只需在数据包写上对方的 mac 地址,再通过交换机广播到对方的机器上即可。如果跨子网的机器需要进行通信,就要在数据包写上对方的 IP 地址,然后先通过 mac 地址广播到路由器,接着路由器再把数据包传输到路由器,最后让路由器再根据另外一个子网的 IP 地址转换为 mac 地址,然后通过另外一个子网的交换机广播到对方的机器上。
(7)总结
网络层最重要的协议就是 IP 协议,IP 协议定义了一个个的 IP 地址,通过 IP 地址可以划分出一个个的子网。
子网内通信是通过以太网协议 + mac 地址 + 交换机来广播数据包的。
跨子网通信是先通过交换机将数据包广播到路由器(网关)上去,路由器(网关)可能会再进行不断转发到另一个路由器(网关),直至转发到最后一个路由器发现目标机器的 IP 地址和自己是在同一个子网内,最后一个路由器根据 ARP 缓存可以知道目标机器的 IP 地址和对应子网的 mac 地址。所以由最后一个路由器通过以太网协议 + mac 地址 + 交换机,把数据包广播到目标机器的网卡上。
跨子网通信:IP 地址 -> mac 地址 -> 交换机 -> 路由器 -> IP 地址 -> 交换机。
6.传输层(TCP 和 UDP 协议 + Socket + 端口)
网络层基于 IP 协议,可实现一个主机到另一个主机的寻址和通信。
传输层基于 TCP/UDP 协议,可实现一个主机端口到另一个主机端口的连接和通信,这个通信就是通过 Socket 来实现的。
通过 Socket 可以基于 TCP/IP 协议完成基于 IP 地址和 mac 地址的转换和寻址,然后通过路由器通信建立一个端口到另外一个端口的连接。
UDP 和 TCP 都是传输层的协议,作用就是在数据包里加入端口号,这样就可以通过端口号进行点对点的通信。UDP 协议是不可靠的,发出去的数据不确定是否能收到。TCP 协议是可靠的,要进行三次握手,收到数据必须要进行回复。
7.应用层(HTTP 协议 + SMTP 协议)
通过传输层的 TCP 协议可以实现应用间的数据传输,而不同的应用如邮件、网页等都会定义不同的应用层协议。常见的应用层协议有 HTTP 协议、SMTP 协议等,这里的应用层综合了会话层、表示层、应用层。
8.浏览器请求一个域名会发生什么
整体过程:
首先根据域名去找 DNS 服务器获取其对应的 IP 地址,接着用子网掩码去判断本地 IP 地址和域名 IP 地址是否在同一个子网。如果在同一个子网,则根据以太网协议 + mac 地址 + 交换机将数据包广播出去。如果不在同一个子网,则先将数据包发送给网关(路由器),再由网关转发数据包给域名 IP 所在子网的网关。
应传网数:
应用层(HTTP 协议)-> 传输层(TCP 协议) -> 网络层(IP 协议) -> 数据链路层(以太网协议)。
步骤一:
浏览器请求一个域名,先按照应用层的 HTTP 协议,将 HTTP 请求报文封装成一个 HTTP 数据包,此时的 HTTP 数据包是没有头的。
步骤二:
接着 HTTP 数据包来到传输层,该层的 TCP 协议会 HTTP 给数据包设置端口。也就是会把 HTTP 数据包封装到一个 TCP 数据包上,并且添加一个 TCP 头,这个 TCP 头会包含发送者和接收者的端口。
步骤三:
接着 TCP 数据包来到网络层,该层的 IP 协议会给 TCP 数据包设置 IP 地址。也就是会把 TCP 头和 TCP 数据包封装到一个 IP 数据包里,并且添加一个 IP 头,这个 IP 头里会包含发送者和接收者的 IP 地址。通过 IP 协议,可以判断发送者和接收者的 IP 地址是否是在同一个子网的。
步骤四:
接着 IP 数据包来到数据链路层,该层的以太网协议会给 IP 数据包设置 mac 地址。也就是把 IP 头和 IP 数据包封装到一个以太网数据包里,并且添加一个以太网头。这个以太网头里会包含发送者和接收者的网卡的 mac 地址,以太网数据包的大小限制是 1500 字节。
步骤五:
以太网数据包会通过交换机被发送到网关(路由器),网关(路由器)又会将数据包发送给别的子网,最后到达服务器接收者。
9.TCP 三次握手建立连接的过程
在 Socket 编程中,三次握手的过程会由客户端执行 connect()方法来触发。
第一次握手:
客户端发送连接请求报文:ACK = 0、SYN = 1、seq = x。当客户端发出连接请求报文后,会处于 SYN_SENT 状态,等待服务器的响应。
第二次握手:
服务端收到 SYN = 1 的连接请求报文后,需要返回一个确认报文:ACK = 1、SYN=1、ack = x + 1、seq = y。当服务端发出确认报文后,会处于 SYN_RECV 状态。
第三次握手:
客户端收到 ack = x + 1 的确认报文后,会继续发送一个确认报文:ACK = 1、ack = y + 1、seq = x + 1。当服务端收到 ack = y + 1 的报文后,则说明连接建立成功,此时客户端和服务端都进入 ESTABLISHED 状态。
三次握手就是三次请求,所以每次握手都会进行:封装 TCP 数据包、封装 IP 数据包、封装以太网数据包,然后通过"IP 地址 -> mac 地址 -> 交换机 -> 路由器 -> IP 地址 -> mac 地址 -> 交换机"的方式进行数据传输。

10.如果 TCP 建立连接只握手两次
一.假如只需要两次握手就可以建立连接
如果客户端第一次握手发送过去,结果卡在某个地方没及时到达服务端。那么客户端会再次重试发送第一次握手过去,然后服务端收到了重发的第一次握手,于是返回第二次握手建立了连接。
当客户端和服务端完成通信后,原来卡在某个地方的第一次握手发到了服务端,于是服务端又返回一个第二次握手,并且开辟资源准备和客户端进行通信。
但是客户端此时已经通信完成了,不会再发送数据给服务端,从而造成服务端无效地开辟资源。
二.假如需要三次握手才可以建立连接
此时面对上述情况,客户端收到服务端延迟的第二次握手时就会发现不对,于是就可借助第三次握手发送复位报文告诉服务端撤销开辟的资源。此外由于 3 次握手就够了,所以不需要 4 次或 5 次浪费资源了。
11.TCP 断开连接的四次挥手
第一次挥手:
客户端发送连接释放报文:FIN=1、seq=u,然后进入 FIN-WAIT-1 状态。
第二次挥手:
服务端收到报文后进入 CLOSE_WATI 状态,然后返回客户端一个确认报文:ACK=1、ack=u+1、seq=v。客户端收到确认报文后进入 FIN-WAIT-2 状态,此时从客户端到服务端的连接就释放了。
第三次挥手:
服务端发送连接释放报文:FIN=1、ack=u+1、seq=w,然后进入 LAST-ACK 状态。其中序列号 seq 是 w 而不是 v+1,是因为服务端很可能又发送了一些数据。
第四次挥手:
客户端收到连接释放报文后,发送应答报文:ACK=1、ack=w+1、seq=u+1。然后进入 TIME_WAIT 状态,再等一会便会进入 CLOSED 状态,服务端收到应答报文后也会进入 CLOSED 状态。

由于 TCP 连接是全双工的,因此每个方向都必须要单独进行关闭。当一方完成数据发送任务后,需要发送一个 FIN 来终止这一方向的连接。收到一个 FIN 只是意味着这一方向上没有数据流动了也就是不会再收到数据,但在这个 TCP 连接上仍然可能会发送数据除非也发送一个 FIN 回去。
12.Socket 编程和 TCP/IP 协议的关系
(1)Socket 通信的原理
首先服务端会有一个 ServerSocket 无限等待客户端来进行连接。然后某个客户端如果要跟服务端连接,就需要在客户端本地创建一个 Socket 去连接服务端。接着建立连接后,服务端上的 ServerSocket 也会创建出一个 Socket,这样就可以由客户端的 Socket 跟服务端的 Socket 进行通信。
其中连接的建立和释放,都是基于 TCP 三次握手和四次挥手来实现的。其中数据的传输是基于 TCP 协议的:封装 TCP 数据包 + TCP 头(端口) -> 封装 IP 数据包 + IP 头(IP 地址) -> 封装以太网数据包 + 以太网头(mac 地址)。
(2)Socket 编程的核心方法
TCP 服务端:socket() -> bind() -> listen() -> accept() -> read() -> write() -> close()。
TCP 客户端:socket() -> connect() -> write() -> read() -> close()。
13.HTTP 的工作原理
(1)请求报文和响应报文的结构
请求报文的结构:请求行(方法 + 地址 + 版本)、请求头、请求体。
响应报文的结构:响应行(版本 + 状态码 + 原因)、响应头、响应体。
(2)HTTP 工作原理
经过 TCP 三次握手建立 TCP 连接后,会先将 HTTP 请求封装到应用层数据包,再封装到 TCP 数据包,再封装到 IP 数据包,最后封装到以太网数据包。
如果以太网数据包过大,那么会拆成几个包,然后通过以太网协议 + mac 地址 + 交换机将数据包广播到网关(路由器)上。
以太网数据包来到网关后,又会经过多个网关进行转发,最后到达目标机器。接着通过一层层拆包获取到 HTTP 请求报文,交给应用程序进行处理。
最后目标机器会将 HTTP 响应按原路径返回给客户端,之后经过 TCP 四次挥手断开 TCP 连接。
(3)HTTP 1.0
默认是短连接,也就是底层的 TCP 连接是短连接。比如一个网页要向服务端发送 30 个请求,则要进行 30 次 TCP 连接的建立和释放。
(4)HTTP 1.1
默认是长连接,也就是底层的 TCP 连接是长连接。比如浏览器打开一个网页后,底层的 TCP 连接就保持着,不会马上断开。后续该网页发送的这 30 个请求就会共用一个 TCP 连接。
HTTP 本身其实是没有所谓的长连接和短连接之说,HTTP 是长连接还是短连接指的是,底层的 TCP 连接是长连接还是短连接。
HTTP 服务一般用短连接,请求量大但每个请求不会频繁操作一般用短连接。数据库的连接一般用长连接,连接池一般使用的也都是长连接,而 Dubbo://协议是基于长连接的。
14.HTTPS 的实现原理
HTTPS 的工作原理会在 TCP 连接 3 次握手的基础上附加一些信息。
步骤一:客户端把支持的加密规则发送给服务端。
步骤二:服务端从这套加密规则里选出一套加密算法和 Hash 算法,然后把自己的身份信息用证书的方式发给客户端,证书里会有服务端的地址、加密公钥、证书颁发机构。
步骤三:客户端收到消息后会验证证书的合法性,然后会生成一串随机数密码,并用证书里的公钥进行非对称加密,接着使用约定好的 Hash 算法生成握手消息的 Hash 值,然后用随机密码对消息进行对称加密,最后把加密数据、加密后的随机密码、Hash 值发给服务端。
步骤四:服务端收到消息后会从中取出公钥加密后的随机密码,然后用本地的私钥对公钥加密后的随机密码进行解密取出随机密码。接着用随机密码解密客户端发来的握手消息,然后计算握手消息的 Hash 值并验证是否与客户端发过来的 Hash 值一致,最后用随机密码加密一段握手消息发给客户端。
步骤五:客户端拿到消息后,解密握手消息,然后计算消息的 Hash 值。如果计算的 Hash 值与服务端发来的 Hash 值一样,则握手结束。之后所有的数据都会由之前生成的随机密码,通过对称加密来加密。
非对称加密:加密的时候是用了一个公钥去加密,然后解密的时候是用私钥去解密。
对称加密:加密的时候用的算法,跟解密的时候用的算法是一样的。
常用的非对称加密算法是 RSA 算法,常用的对称加密算法是 AES、RC4 等,常用的 Hash 加密算法就是 MD5。
15.全双工和半双工
全双工和半双工主要发生在交换机层面的。
如果是全双工,发送数据和接收数据可以同时进行。如果是半双工,同一时刻要么只能发送数据、要么只能接收数据。
使用半双工,需要先判断是否有数据正在接入,避免出现信号碰撞。使用全双工,不需要处理信号碰撞的问题,直接发送数据出去即可。
16.Java 进行 IO 读写的底层流程
用户程序进行 IO 读写,基本上会用到系统调用 read 和 write。系统调用 read 把数据从内核缓冲区(内核空间)复制到进程缓冲区(用户空间),系统调用 write 把数据从用户缓冲区(用户空间)复制到内核缓冲区(内核空间)。它们并不等价数据在内核缓冲区和磁盘之间的交换。

Java 服务端处理网络请求的典型过程:
步骤一:接收客户端请求
Linux 通过网卡,读取客户断的请求数据,将数据读取到内核缓冲区。
步骤二:获取请求数据
服务端从内核缓冲区读取数据到 Java 进程缓冲区。
步骤三:服务器端业务处理
Java 服务端在自己的用户空间中处理客户端请求。
步骤四:服务器端返回数据
Java 服务端已构建好的响应,从 Java 进程缓冲区写入内核缓冲区。
步骤五:发送数据给客户端
Linux 内核通过网络 IO ,将内核缓冲区中的数据,写入网卡。网卡通过底层的通讯协议,会将数据发送给目标客户端。
17.同步和异步 + 阻塞和非阻塞
同步和异步关注的是:是否亲自等消息、是否由其他线程告知自己。阻塞和非阻塞关注的是:当前事情还没做好时,是否还能做其他事情。
同步阻塞:事情没做好时,亲自等该事情做好的消息,且不做其他事情。
同步非阻塞:事情没做好时,会做其他事情,但会经常亲自等消息,轮询该事情是否做好。
异步阻塞:事情没做好时,不会做其他事情,但也不会亲自等消息,而是由其他线程告知。
异步非阻塞:事情没做好时,会做其他事情,也不会亲自等消息,而是由其他线程告知。
18.Linux 的常用 IO 模型
一.阻塞 IO 模型
进程(用户线程)会在用户空间中执行 recvfrom 系统调用,recvfrom 会一直等待直到数据已经从内核空间的内核缓冲区复制到用户空间的用户缓冲区或者发生错误时才返回。
所以进程(用户线程)从执行 recvfrom 系统调用开始,到 recvfrom 系统调用返回数据的这段时间内,都是阻塞的。
优点:程序简单,用户线程不会占用 CPU 资源。
缺点:一条线程维护一个连接,不适合高并发。
二.非阻塞 IO 模型
进程(用户线程)会在用户空间中执行 recvfrom 系统调用,如果用户空间的用户缓冲区没有数据,那么 recvfrom 就不要等了,而是直接返回一个错误,从而不阻塞进程(用户线程)。
但进程(用户线程)此后要不断轮询用户空间的用户缓冲区是否已经准备好数据,所以一般不推荐使用。
特点:进程(用户线程)要不断轮询 recvfrom 系统调用,看数据是否已经准备好,直到数据准备好已完成 recvfrom 系统调用为止。
优点:不会阻塞用户线程。
缺点:需要轮询系统调用,占用 CPU 资源。
三.IO 复用模型
IO 多路复用模型就是通过一种新的系统调用,让一个进程(用户线程)可以监视多个连接(文件描述符),一旦某个连接就绪(文件描述符可读可写),那么内核就会通知进程(用户线程)进行相应的 IO 系统调用。
IO 复用模型:两次调用、两次返回,同步 IO、阻塞 IO。该模型中首先执行的不是 recvfrom 系统调用,而是 select/epoll 系统调用。
步骤一:执行 select/epoll 系统调用查询可以读的连接(就绪的文件描述符)
此时内核会查询所有可以查询的 socket 列表。当任何一个 socket 中的数据准备好了之后,select/epoll 系统调用就会返回。所以当进程(用户线程)调用了 select/epoll 系统调用时,是会被阻塞的;
步骤二:进程(用户线程)获得了目标连接(准备好数据的 socket)后,会发起 recvfrom 系统调用,此时进程(用户线程)也会被阻塞。此时数据会从内核缓冲区复制到用户缓冲区,recvfrom 系统调用会将数据返回给进程(用户线程)。
select/poll 是顺序扫描文件描述符 fd 是否就绪。epoll 是用事件驱动代替顺序扫描,当文件描述符 fd 就绪时,立即进行回调。
优点:select/epoll 可以同时处理成千上万个连接,不必每个连接创建一个线程。
缺点:select/epoll 系统调用还是属于同步阻塞 IO。
19.IO 多路复用技术
IO 多路复用技术通过把多个 IO 的阻塞复用到同一个 select 的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。
与传统的多线程/多进程模型相比:IO 多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或线程,也不需要维护这些线程和进程的运行,降低了系统维护的工作量,节省了系统资源。
20.select、poll、epoll 的区别
区别一:
epoll 支持一个进程打开的 socket 描述符 fd 的数量不受限制。select 最大的缺陷是单个进程所打开的 socket 描述符 fd 的数量是有限制的,默认 1024。epoll 在 1G 内存的机器上可以打开 10 万左右的连接,内存越大,打开的连接越多。
区别二:
epoll 的 IO 效率不会随着 fd 数目的增加而线性下降。select/poll 每次调用都会线性扫描全部 socket 集合,导致效率线性下降。epoll 是根据每个 fd 上面的回调函数实现的,只有活跃的 socket 才会主动调用回调函数。
区别三:
epoll 使用 mmap 加速内核与用户空间的消息传递。为了能让内核把 fd 消息通知给用户空间,select 和 poll 需要通过内存复制来实现。epoll 则通过 mmap 让内核和用户空间共享一块内存来避免内存复制。
文章转载自:东阳马生架构
评论