【Java网络编程】常用的网络模型

常用的网络模型

1.1 BIO 模型

网络编程的基本模型是 C/S 模型,即两个进程间的通信。服务端提供 IP 和监听端口,客户端通过连接操作想服务端监听的地址发起连接请求,通过三次握手连接,如果连接成功建立,双方就可以通过套接字进行通信。传统的同步阻塞模型开发中,ServerSocket 负责绑定 IP 地址,启动监听端口;Socket 负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。 简单的描述一下 BIO 的服务端通信模型:采用 BIO 通信模型的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成后,通过输出流返回应答给客户端,线程销毁。即典型的一请求一应答通宵模型。

主线程负责监听当有新的连接的时候创建一个新的子线程处理任务。如下图所示:

在这里插入图片描述

缺点:

该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈 1:1 的正比关系,Java 中的线程也是比较宝贵的系统资源,线程数量快速膨胀后,系统的性能将急剧下降,随着访问量的继续增大,系统最终就死掉了。

BIO模型代码演示:


1.2 NIO 模型

  • NIO:Non-blocking I/O 或 New I/O。
  • 从特性出发:可以使用非阻塞和阻塞两种模式。
  • 从年龄出发:新 IO,在原始 IO 之后出现的。
  • 诞生:从 JDK 1.4 引入的全新的输入输出标准库,重新设计的一套 IO 标准。
  • 职务:高并发网络服务器支持岗。作为原始 IO 的补充,为了应对高性能高并发的应用场景。

NIO 和 BIO 的比较:

在这里插入图片描述

  • 1、BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 I/O 的效率比流 I/O 高很多。BIO基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
  • 2、BIO 是阻塞的,NIO 可以设置为非阻塞模式。
  • 3、Selector(选择器)用于监听多个通道的事件(比如:连接请求,数据到达等),当具体事件发生了之后才会进行相应的处理不会出现阻塞情况。

1.2.1 Selector

Selector 称为选择器 ,当然你也可以翻译为 多路复用器 。它是 Java NIO 核心组件中的一个,用于检查一个或多个 NIO Channel(通道)的状态是否处于可读、可写。Selector 提供选择已经就绪的任务的能力:Selector 会不断检测注册在其上的 Channel,如果某个 Channel 上面发生读或者写事件,这个Channel 就处于就绪状态,会被 Selector 轮询出来,然后通做出相对应的操作。

在这里插入图片描述

那么事件都有哪些呢?NIO 所有监听的事件都定义在 SelectionKey 下。

在这里插入图片描述

  • OP_ACCEPT 就绪条件:当收到一个客户端的连接请求时,该操作就绪。这是 ServerSocketChannel 上唯一有效的操作。
  • OP_CONNECT 就 绪 条 件 : 只 有 客 户 端 SocketChannel 会 注 册 该 操 作 , 当 客 户 端 调 用 SocketChannel.connect() 时,该操作会就绪。但是连接就绪不代表连接成功。所以还要通过 finishConnect 方法判断连接是否建立成功
  • OP_READ 就绪条件:该操作对客户端和服务端的 SocketChannel 都有效,当 OS 的读缓冲区中有数据可读时,该操作就绪。
  • OP_WRITE 就绪条件:该操作对客户端和服务端的 SocketChannel 都有效,当 OS 的写缓冲区中有空闲的空间时(大部分时候都有),该操作就绪。一般情况,不应该注册 OP_WRITE 事件,OP_WRITE 的就绪条件为操作系统内核缓冲区有空闲空间(OP_WRITE事件是在Socket发送缓冲区中的可用字节数大于或等于其低水位标记 SO_SNDLOWAT 时发生),而写缓冲区绝大部分事件都是有空闲空间的,所以当你注册写事件后,写操作一直就是就绪的,这样会导致 Selector 处理线程会占用整个 CPU 的资源。所以最佳实践是当你确实有数据写入时再注册 OP_WRITE 事件,并且在写完以后马上取消注册。

判断是否有事件就绪:

  • int select() : 阻塞到至少有一个通道在你注册的事件上就绪了。
  • int select(long timeout) : 和 select()一样,除了最长会阻塞 timeout 毫秒(参数)。
  • int selectNow() : 不会阻塞,不管什么通道就绪都立刻返回(译者注:此方法执行非阻塞的选择操作。如果自从前一次选择操作后,没有通道变成可选择的,则此方法直接返回零。)

注册事件:

Channel.register(selector, SelectionKey. OP_ACCEPT );

注册多个事件:

Channel.register(selector, SelectionKey. OP_ACCEPT | SelectionKey.OP_WRITE);

判断自己所关注的事件:

int interestSet = selectionKey. readyOps();
boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;

可以和 interest set 一样通过 readySet 值的位与来得出哪些操作可以执行,也可以直接调用方法。

selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();

1.2.2 缓冲区(Buffer)

缓冲区是包在一个对象内的基本数据元素数组。Buffer 类相比一个简单数组的优点 是它将关于数据的数据内容和信息包含在一个单一的对象中。Buffer 类以及它专有的子类定义了 一个用于处理数据缓冲区的 API。

根据数据类型的不同提供了不同的类型的 buffer,除了 Bollean 之外各种类型都有提供:

ByteBuffer;
IntBuffer;
CharBuffer;
LongBuffer;
FloatBuffer;
ShortBuffer;
DoubleBuffer;

常用属性:

在这里插入图片描述

  • position:

Buffer 可操作的第一个位置。在往 Buffer 中写数据时会从 Buffer 数组中的 position 位置开始写。从 Buffer 中读数据时会从 Buffer 的 position 开始读。

  • limit:

Buffer 最多可操作的数据的位置。在往 Buffer 中写数据时表示最多可写到数据量为 limit。从 Buffer中读数据时需要开启 Buffer 的读模式,读从 position 到 limit 位置的数据(一般是调用 flip 方法使 limit=position; poslition=0)。

  • capcity

Buffer 数组的容量。

  • mark

备忘位置,调用 mark()使得 mark=position,调用 reset(),恢复 postion 使 position=mark。

下面我们通过代码一起来看看缓冲区的特点:

public class NIOBufferTest <
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值