Netty学习——前置知识 网络模型

目的:梳理在学习Netty的过程中需要用到的网络知识,为了在学习Netty的过程中更加容易理解知识。

网络模型

为啥要划分网络模型?

网络是一个非常复杂的系统,对于复杂的系统,我们都需要尽可能的分为多个小的,功能独立的模块或者子系统,这样我们就可以将注意力集中在这个复杂系统的某个特定的部分,这就是模块化的思想。因为计算机网络要解决的问题非常大,非常复杂,所以需要利用模块化的思想将其划分为多个模块来处理和设计。人们发现层次式的模块划分方法特别适合网络系统,因此目前所有的网络系统都采用分层的体系结构。

网络模型有几种主流分层方式?

OSI七层模型 && TCP/IP四层模型 && TCP/IP五层模型

对于TCP/IP五层模型 来说java的网络编程一般涉及应用层、传输层、网络层

  1. 应用层
    • 作用 : 应用层是最接近用户的一层,也是开发者直接交互最多的网络层次。应用层决定了通信的目的和内容。
      • 打个比喻像我们寄快递将要寄的东西和目的地交给快递员(传输层),
      • 从系统层次上讲,应用层是在操作系统的用户态,传输层及以下则工作在内核态
    • 直接操作:Java网络编程最主要涉及的层
    • 典型协议/实现:
      • HTTP/HTTPS(HttpURLConnectionHttpClient、Netty的HttpServerCodec
      • WebSocket(javax.websocket、Netty的WebSocketServerProtocolHandler
      • FTP、SMTP等协议客户端
      • 自定义应用层协议(通过Socket或Netty实现)
  2. 传输层
    • 应⽤层的数据包会传给传输层, 传输层(Transport Layer)是为应⽤层提供⽹络⽀持的
    • 核心操作层 :Java最常显式编程的层
      • 关键实现:
        • 传输控制协议-TCP(Socket/ServerSocket、Netty的NioSocketChannel
        • UDP(DatagramSocket、Netty的NioDatagramChannel
      • 特性控制:
        • 端口绑定(bind(int port)
        • 连接超时设置
        • 缓冲区大小配置(SO_RCVBUF/SO_SNDBUF)
  3. 网络层
    • 我们不希望传输层协议处理太多的事情,只需要服务好应⽤即可,让其作为应⽤间数据传输的媒介,帮助实现应⽤到应⽤的通信,⽽实际的传输功能就交给下⼀层,也就是⽹络层
    • 间接操作:通常由JVM和操作系统处理,但可配置
    • 相关操作:
      • IP协议选择(IPv4/IPv6,通过java.net.preferIPv6Addresses系统属性)
      • 路由控制(通过操作系统级配置)
      • 在Netty中可通过ChannelOption.IP_TOS设置服务类型

关于网络分层的数据结构

⽹络接⼝层的传输单位是帧(frame), 【网络层】IP 层的传输单位是包(packet), 【传输层】TCP 层的传输单位是段(segment),【应用层】 HTTP 的传输单位则是消息或报⽂(message)。但这些名词并没有什么本质的区分,可以统称为数据包。

IO模型

步骤:

  1. 应用A把消息发送到 TCP发送缓冲区
  2. TCP发送缓冲区再把消息发送出去,经过网络传递后,消息会发送到B服务器的TCP接收缓冲区
  3. B再从TCP接收缓冲区去读取属于自己的数据

IO也分为不同的类型

1、阻塞IO

阻塞IO:在应用调用recvfrom读取数据时,其系统调用直到数据包到达且被复制到应用缓冲区中或者发送错误时才返回,在此期间一直会等待,进程从调用到返回这段时间内都是被阻塞的。

阻塞IO的流程: 1、应用进程向内核发起recvfrom读取数据 2、准备数据报(应用进程阻塞) 3、将数据从内核复制到应用空间 4、复制完成后,返回成功提示

2、非阻塞Io

定义:非阻塞IO是在应用调用recvfrom读取数据时,如果该缓冲区没有数据的话,就会直接返回一个EWOULDBLOCK错误,不会让应用一直等待中。在没有数据的时候会即刻返回错误标识,那也意味着如果应用要读取数据需要不断的调用recvfrom请求,直到读取到它数据要的数据为止。 非阻塞IO的流程:

  1. 应用进程向内核发起recvfrom读取数据
  2. 没有数据报准备好,即刻返回EWOULDBLOCK错误码
  3. 应用进程向内核发起recvfrom读取数据
  4. 已有数据包准备好就进行下一步骤,否则还是返回错误码
  5. 将数据从内核拷贝到用户空间
  6. 完成后,返回成功提示

3、IO复用模型

当B从TCP缓冲区中读取数据,如果在并发的环境下,可能会N个发送方向应用B发送消息,这种情况下我们的应用就必须创建多个线程去读取数据,每个线程会自己调用

这种高并发的情况,如果仅仅用普通的IO模型就会造成大量的资源浪费,甚至是扛不住这种频繁的请求导致服务器崩掉。

问题的解决

进程通过将一个或多个fd传递给select,阻塞在select操作上,select帮我们侦测多个fd是否准备就绪,当fd准备就绪时,select返回数据可读状态,应用程序再调用recvfrom读取数据。

复用IO的基本思路就是通过select或poll、epoll 来监控多fd ,来达到不必为每个fd创建一个对应的监控线程,从而减少线程资源创建的目的。

4、信号驱动IO模型

上述的IO复用模型,就可以从图上发现一个问题,select是采用轮询的方式来监控多个fd,这种无脑的轮询显得没有必要,极为浪费资源,大部分的轮询都是无效的。
因此进一步改进,数据准备就绪后,就通知接收,就是所谓的信号驱动IO模型

术语描述:首先开启套接口信号驱动IO功能,并通过系统调用sigaction执行一个信号处理函数,此时请求即刻返回,当数据准备就绪时,就生成对应的进程SIGIO信号,通过信号回调通知应用线程调用recvfrom来读取数据。

IO复用模型里面的select虽然可以监控多个fd了,但select其实现的本质上还是通过不断的轮询fd来监控数据状态, 因为大部分轮询请求其实都是无效的,所以信号驱动IO意在通过这种建立信号关联的方式,实现了发出请求后只需要等待数据就绪的通知即可,这样就可以避免大量无效的数据状态轮询操作。

5、异步IO

不管是IO复用还是信号驱动,我们要读取一个数据总是要发起两阶段的请求,第一次发送select请求,询问数据状态是否准备好,第二次发送recevform请求读取数据。

应用只需要向内核发送一个read 请求,告诉内核它要读取数据后即刻返回;内核收到请求后会建立一个信号联系,当数据准备就绪,内核会主动把数据从内核复制到用户空间,等所有操作都完成之后,内核会发起一个通知告诉应用

应用告知内核启动某个操作,并让内核在整个操作完成之后,通知应用,这种模型与信号驱动模型的主要区别在于,信号驱动IO只是由内核通知我们合适可以开始下一个IO操作,而异步IO模型是由内核通知我们操作什么时候完成。 异步IO的优化思路是解决了应用程序需要先后发送询问请求、发送接收数据请求两个阶段的模式,在异步IO的模式下,只需要向内核发送一次请求就可以完成状态询问和数拷贝的所有操作。

相关推荐
喵手9 分钟前
StringUtils 工具类实战详解,你还不进来学习!
java·后端·java ee
喵手12 分钟前
如何快速实现文件上传、下载与读写操作?FileUtils有话要说~
java·后端·java ee
DongLi0115 分钟前
Rust 变量和可变性
后端
陈随易36 分钟前
一段时间没写文章了,花了10天放了个屁
前端·后端·程序员
星星电灯猴39 分钟前
抓包工具分析接口跳转异常:安全校验误判 Bug 全记录
后端
调试人生的显微镜40 分钟前
后台发热、掉电严重?iOS 应用性能问题实战分析全过程
后端
深栈解码1 小时前
OpenIM 源码深度解析系列(十八):附录二数据库结构
后端
前端付豪1 小时前
Google Ads 广告系统排序与实时竞价架构揭秘
前端·后端·架构
努力的小郑1 小时前
MySQL DATETIME类型存储空间详解:从8字节到5字节的演变
后端
哪吒编程2 小时前
我的第一个AI编程助手,IDEA最新插件“飞算JavaAI”,太爽了
java·后端·ai编程