Java 网络编程 —— 非阻塞式编程

这篇具有很好参考价值的文章主要介绍了Java 网络编程 —— 非阻塞式编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

线程阻塞概述

在生活中,最常见的阻塞现象是公路上汽车的堵塞。汽车在公路上快速行驶,如果前方交通受阻,就只好停下来等待,等到公路顺畅,才能恢复行驶。

线程在运行中也会因为某些原因而阻塞。所有处于阻塞状态的线程的共同特征:放弃 CPU,暂停运行,只有等到导致阻塞的原因消除,才能恢复运行,或者被其他线程中断该线程会退出阻塞状态,并且抛出 InterruptedException

导致线程阻塞的原因主要有以下方面:

  • 线程执行了 Threadsleep(int n) 方法,线程放弃 CPU,睡眠 n ms,然后恢复运行
  • 线程要执行一段同步代码,由于无法获得相关的同步锁,只好进入阻塞状态,等到获取同步锁再恢复运行
  • 线程执行了一个对象的 wait() 方法,进入阻塞状态,只有等到其他线程执行了该对象的 notify()notifyAll() 方法,才可能将其唤醒
  • 线程执行 IO 操作或进行远程通信时,会因为等待相关的资源而进入阻塞状态

进行远程通信时,在客户程序中,线程在以下情况下可能进入阻塞状态:

  • 请求与服务器建立连接时,即当线程执行 Socket 的带参数的构造方法,或执行 Socke 的 connect() 方法时,会进入阻塞状态,直到连接成功,此线程才从 Socket 的构造方法或 connect() 方法返回

  • 线程从 Socket 的输入流读入数据时,如果没有足够的数据,就会进入阻塞状态,直到读到了足够的数据,或者到达输入流的末尾,或者出现了异常,才从输入流的 read() 方法返回或异常中断

    输入流中有多少数据才算足够呢?这要看线程执行的 read() 方法的类:

    • int read():只要输入流中有 1 字节,就算足够
    • int read(byte[] buf):只要输入流中的字节数目与参数 buff 数组的长度相同,就算足够
    • String readLine():只要输入流中有 1 行字符,就算足够
  • 线程向 Socket 的输出流写一批数据时,可能会进入阻塞状态,等到输出了所有的数据,或者出现异常,才从输出流的 write() 方法返回或异常中断

  • 如果调用 Socket 的 setSoLinger() 方法设置了关闭 Socket 的延迟时间,那么当线程执行 Socket 的 close() 方法时,会进入阻塞状态,直到底层 Socket 发送完所有剩余数据或者超过了 setSoLinger() 方法设置的延迟时间,才从 close() 方法返回

在服务器程序中,线程在以下情况下可能会进入阻塞状态:

  • 线程执行 ServerSocket 的 accept() 方法,等待客户的连接,直到接收到了客户连接才从 accept() 方法返回
  • 线程从 Socket 的输入流读入数据时,如果输入流没有足够的数据就会进入阻塞状态
  • 线程向 Socket 的输出流写一批数据时,可能会进入阻塞状态,等到输出了所有的数据,或者出现异常,才从输出流的 write() 方法返回或异常中断

由此可见,无论是在服务器程序还是客户程序中,当通过 Socket 的输入流和输出流读写数据时,都可能进入阻塞状态。这种可能出现阻塞的输入和输出操作被称为阻塞 IO。与此对照,如果执行输入和输出操作时,不会发生阻塞,则称为非阻塞 IO


非阻塞通信的基本思想

假如同时要做两件事:烧开水和煮粥

烧开水的步骤如下:

锅子里放水,打开煤气炉
等待水烧开 // 阻塞
关闭煤气炉,把开水灌到水壶里

煮粥的步骤如下:

锅子里放水和米,打开煤气炉
等待粥煮开 // 阻塞
调整煤气炉,改为小火
等待粥煮熟 // 阻塞
关闭煤气炉

为了同时完成两件事,一种方案是同时请两个人分别做其中的一件事,这相当于采用多线程来同时完成多个任务。还有一种方案是让一个人同时完成两件事,这个人应该善于利用一件事的空闲时间去做另一件事,这个人一刻也不应该闲着:

锅子里放水,打开煤气炉 // 开始烧开水
锅子里放水和米,打开煤气炉 // 开始煮粥
while(一直等待,直到有水烧开、粥煮开或粥煮熟事件发生) { // 阻塞
	if(水烧开)
		关闭煤气炉,把开水灌到水壶里;
	if((粥煮开)
		调整煤气炉,改为小火;
	if(粥熟)
		关闭煤气炉;
}

这个人不断监控烧水和煮粥的状态,如果发生了条件中任一事件就去处理,处理完一件事后继续监控,直到所有的任务都完成

以上工作方式也可以被运用到服务器程序中,服务器程序只需要一个线程就能同时接收客户的连接、接收各个客户发送的数据,以及向各个客户发送响应数据。服务器程序的处理流程如下:

while(一直等待,直到有接收连接就绪事件、读绪事件或写就绪事件发生) { //阻塞
	if(有客户连接)
		接收客户的连接; // 非阻塞
	if(某个socket的输入流中有可读数据)
		从输入流中读数据; // 非阻塞
	if(某个socket的输出流可以写数据)
		向输出流写数据; // 非阻塞
}

以上处理流程采用了轮询的工作方式,当某一种操作就绪,就执行该操作,否则就查看是否还有其他就绪的操作可以执行。线程不会因为某一个操作还没有就绪,就进入阻塞状态,一直傻傻地在那里等待这个操作就绪

为了使轮询的工作方式顺利进行,接收客户的连接、从输入流读数据,以及向输出流写数据的操作都应该以非阻寒的方式运行。所谓非阻塞,指当线程执行这些方法时,如果操作还没有就绪,就立即返回,而不会一直等到操作就绪


非阻塞通信 API

java.nio.channels 包提供了支持非阻塞通信的类,如下所述:

  • ServerSocketChannelServerSocket 的替代类,支持阻塞通信与非阻塞通信
  • SocketChannelSocket 的替代类,支持阻塞通信与非阻塞通信
  • Selector:为 ServerSocketChannel 监控接收连接就绪事件,为 SocketChannel 监控连
    接就绪、读就绪和写就绪事件
  • SelectionKey:代表 ServerSocketChannel 以及 SocketChannelSelector 注册事件的句柄。当一个 SelectionKey 对象位于 Selector 对象的 selected-keys 集合中,就表示与这个 SelectionKey 对象相关的事件发生了

ServerSocketChannelSocketChannel 都是 SelectableChannel 的子类,如图所示,SelectableChannel 类及其子类都能委托 Selector 来监控它们可能发生的一些事件,这种委托过程也被称为注册事件过程

Java 网络编程 —— 非阻塞式编程

ServerSocketChannelSelector 注册接收连接就绪事件的代码如下:

SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

SelectionKey 类的一些静态常量表示事件类型,ServerSockerChamnel 只可能发生一种事件:

  • SelectionKey.OP_ACCEPT:接收连接绪事件,表示至少有了一个客户连接,服务器可以接收这个连接、

SocketChannel 可能发生以下三种事件:

  • SelectionKey.OP_CONNECT:连接就绪事件,表示客户与服务器的连接已经建立成功
  • SelectionKey.OP_READ:读就绪事件,表示输入流中已经有了可读数据,可以执行读操作了
  • SelectionKey.OP_WRITE: 写就绪事件,表示已经可以向输出流写数据了

SocketChannel 提供了接收和发送数据的方法:

  • read(ByteBuffer buffer):接收数据,把它们存放到参数指定的 ByteBuffer
  • write(ByteBuffer buffer):把参数指定的 ByteBuffer 中的数据发送出去

ByteBuffer 表示字节缓冲区,SocketChannelread()write() 方法都会操纵 ByteBufferByteBuffer 类继承于 Buffer 类。ByteBuffer 中存放的是字节,为了把它们转换为字符串还需要用到 Charset 类,Charset 类代表字符编码,它提供了把字节流转换为字符串(解码过程)和把字符串转换为字节流(编码过程)的实用方法

下面分别介绍 BufferCharsetSelectableChannelServerSocketChannelSocketChannelSelectorSelectionKey 的用法


缓冲区 Buffer

数据输入和输出往往是比较耗时的操作,缓冲区从两个方面提高 I/O 操作的效率:

  • 减少实际的物理读写次数
  • 缓冲区在创建时被分配内存,这块内存区域一直被重用,这可以减少动态分配和回收内存区域的次数

java.nio 包公开了 Buffer 类的 API,使得 Java 程序可以直接控制和运用缓冲区,所有的缓冲区都有以下属性:

  • 容量(capacity):表示缓冲区可以保存多少数据
  • 极限(limit):表示缓冲区的当前终点,不能对缓冲区中超过极限的区域进行读写操作
  • 位置(position):表示缓冲区中下一个读写单元的位置

以上三个属性的关系为:容量 > 极限 >= 位置 >= 0

缓冲区提供了用于改变以上三个属性的方法:

// 把极限设为容量,把位置设为0
clear();
// 把极限设为位置,把位置设为 0
flip();
// 不改变极限,把位置设为0
rewind();

Buffer 类的 remaining() 方法返回缓冲区的剩余容量,取值等于 极限 - 位置

Buffer 类的 compact() 方法删除缓冲区内从 0 到当前位置 position 的内容,然后把从当前位置 position 到极限limit 的内容拷贝到 0 到 limit - position 的区域内

java.nio.Buffer 类是一个抽象类,不能被实例化。它共有 8 个具体的缓冲区类,其中最基本的缓冲区是 ByteBuffer,它存放的数据单元是字节,ByteBufer 类并没有提供公开的构造方法,但是提供了两个获得 ByteBuffer 实例的静态工厂方法:

// 返回一个ByteBuffer对象,参数capacity指定缓冲区的容量
allocate(int capacity);
// 返回一个ByteBuffer对象,参数capacity指定缓冲区的容量
// 该方法返回的缓冲区被称为直接缓冲区,能进一步提高 I/O 操作的速度
// 分配直接缓冲区的系统开销很大,因此只有在缓冲区较大并且长期存在,或经常重用时,才使用该缓冲区
directAllocate(int capacity);

除 boolean 类型以外,每种基本类型都有对应的缓冲区类,包括 CharBufferDoubleBufferFloatBufferIntBufferLongBufferShortBuffer。在 CharBuffer 中存放的数据单元为字符,以此类推。还有一种缓冲区是 MappedByteBuffer,它是 ByteBuffer 的子类,能够把缓冲区和文件的某个区域直接映射

所有具体缓冲区类都提供了读写缓冲区的方法:

// 相对读,从缓冲区的当前位置读取一个单元的数据,读完后把位置加1
get();
// 绝对读,从参数 index 指定的位置读取一个单元的数据
get(int index);
// 相对写,向缓冲区的当前位置写一个单元的数据,写完后把位置加1
put(单元数据类型 data);
// 绝对写,向参数index指定的位置写入一个单元的数据
put(int index, 单元数据类型 data);

ByteBuffer 类不仅可以读取和写入一个单元的字节,还可以读取和写入 int、char、float 和 double 等基本类型的数据,例如:

getInt()
getInt(int index)

以上不带 index 参数的方法会在当前位置读取或写入数据,称为相对读写。带 index 参数的方法会在 index 参数指定的位置读取或写入数据,称为绝对读写


字符编码 Charset

java.nio.Charset 类的每个实例代表特定的字符编码类型,把字节序列转换为字符串的过程称为解码,把字符串转换为字节序列的过程称为编码

Charset 类提供了编码与解码的方法:

// 对参数str指定的字符串进行编码,把得到的字节序列存放在一个ByteBuffer对象并将其返回
ByteBuffer encode(String str);
// 对参数cb指定的字符缓冲区中的字符进行编码,把得到的字节序列存放在一个ByteBuffer对象并将其返回
ByteBuffer encode(CharBuffer cb);
// 对参数bb指定的ByteBuffer的字节序列进行解码,把得到的字符序列存放在一个CharBuffer对象并将其返回
CharBuffer decode(ByteBuffer bb);

Charset 类的静态 forName(String encode) 方法返回一个 Charset 对象,参数 encode 指定编码类型。例如以下代码创建了一个代表 GBK 编码的 Charset 对象

Charset charset = Charset.forName("GBK");

Charset 类还有一个静态方法 defaultCharset(),它返回代表本地平台的默认字符编码的 Charset 对象


通道 Channel

通道(Channel)用来连接缓冲区与数据源或数据汇(即数据目的地),数据源的数据经过通道到达缓冲区,缓冲区的数据经过通道到达数据汇

Channel 的主要层次结构如下:

Java 网络编程 —— 非阻塞式编程

java.nio.channels.Channel 接口只声明了两个方法:

// 关闭通道
close();
// 判断通道是否打开
isOpen();

Channel 接口的两个最重要的子接口是 ReadableByteChannelWritableByteChannelReadableByteChannel 接口声明了 read(ByteBuffer dst) 方法,该方法把数据源的数据读入参数指定的 ByteBuffer 缓冲区中。WritableByteChannel 接口声明了 write(ByteBuffer src) 方法,该方法把参数指定的 ByteBuffer 缓冲区中的数据写到数据汇中

ByteChannel 接口是一个便利接口,它扩展了 ReadableByteChannelWritableByteChannel 接口,因而同时支持读写操作

ScatteringByteChannel 接口扩展了 ReadableByteChannel 接口,允许分散地读取数据。分散读取数据指单个读取操作能填充多个缓冲区,ScatteringByteChannel 接口声明了 read(ByteBuffer[] dsts) 方法,该方法把从数据源读取的数据依次填充到参数指定的各个 ByteBuffer

GatheringByteChannel 扩展了 WritableByteChannel 接口,允许集中地写入数据。集中写入数据指单个写操作能把多个缓冲区的数据写到数据, GatheringByteChannel 接口声明了 write(ByteBuffer[] srcs) 方法,该方法依次把参数指定的每个 ByteBuffer 中的数写到数据汇

FileChannel 类是 Channel 接口的实现类,代表一个与文件相连的通道。该类实现了 ByteChannelScatteringByteChannelGatheringByteChannel 接口,支持读操作、写操作、分散读操作和集中写操作。FileChannel 类没有提供公开的构造方法,因此不能用 new 语句来构造它的实例。不过,在FileInputStreamFileOutputStreamRandomAccessFile 类中提供了 getChannel() 方法,该方法返回相应的 FileChannel 对象

SelectableChannel 也是一种通道,它不仅支持阻塞的 I/O 操作,还支持非阻塞的 I/OSelectableChannel 有两个子类,ServerSocketChannelSocketChannelSocketChannel 还实现了 ByteChannel 接口,具有 read(ByteBuffer dst)write(ByteBuffer src) 方法

1. SelectableChannel 类

SelectableChannel 是一种支持阻塞 IO 和非阻塞 IO 的通道。在非阻塞模式下,读写数据不会阻塞,并且 SelectableChannel 可以向 Selector 注册读就绪和写就绪等事件。Selector 负责监控这些事件,等到事件发生时,比如发生了读就绪事件,SelectableChannel 就可以执行读操作了

SelectableChannel 的主要方法如下:

// 当参数block为true,表示把SelectableChannel设为阻塞模式
// 当参数block为false时,表示把SelectableChannel设为非阻塞模式
// SelectableChannel默认采用阻塞模式
// 该方法返回SelectableChannel对象本身的引用,相当于return this
public SelectableChannel configureBlocking(boolean block) throws IOException
// 以下两个方法都向Selector注册事件
public SelectionKey register(Selector sel,int ops) throws ClosedChannelException
public SelectionKey register(Selector sel,int ops,Object attachment) throws ClosedChannelException

以下是 socketChannelSelector 注册读就绪和写就绪事件

SelectionKey key = socketChannel.register(selector.SelectionKey.OP_READ | SelectionKey.OP_WRITE);

register() 方法返回一个 SelectionKey 对象,SeletionKey 被用来跟踪被注册的事件。第二个 register() 方法还有一个 Object 类型的参数 attachment,用于为 SelectionKey 关联附件,当被注册事件发生后,需要处理该事件时,可以从 SelectionKey 中获得这个附件,该附件可用来包含与处理这个事件相关的信息

2. ServerSocketChannel 类

ServerSocketChannel 继承自 SelectableChannel,是 ServerSocket 的替代类,通过它的静态方法 open() 来创建。每个 ServerSockeChannel 对象都与一个 ServerSocket 对象关,通过 socket() 方法返回与它关联的 ServerSocket 对象。可通过以下方式把服务器进程绑定到一个本地端口:

serverSocketChannel.socket().bind(port);

ServerSocketChannel 的主要方法如下:

// 返回一个ServerSocketChannel对象,该对象没有与任何本地端口绑定,并且处于阻塞模式
public static ServerSocketChannel open() throws IOException
// 用于接收客户的连接,如果处于非阻塞状态,当没有客户连接时就立即返回null
public SocketChannel accept() throws IOException
// 返回ServerSocketChannel所能产生的事件,这个方法总是返回SelectionKey.OP_ACCEPT
public final int validOps()
// 返回ServerSocketChannel关联的ServerSocket对象
public ServerSocket socket()

3. SocketChannel类

SockeChannel 可以被看作是 Socket 的替代类,SockeChannel 不仅继承了 SelectableChannel,而且实现了 ByteChannelSockeChannel 同样通过它的静态方法 open() 来创建

public static SocketChannel open() throws IOException
// 带参数的构造方法还会建立与远程服务器的连接
public static SocketChannel open(SocketAddress remote) throws IOException

SocketChannel 的主要方法如下:

// 返回ServerSocketChannel所能产生的事件,这个方法总是返回以下值
// SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE
public final int validOps()
// 返回SocketChannel关联的Socket对象
public Socket socket()
// 建立远程连接,当处于非阻塞模式时,如果立即连接成功返回true,否则返回false
public boolean connect(SocketAddress remote) throws IOException
// 判断底层Socket是否已经建立远程连接
public boolean isConnected()
// 判断是否正在进行远程连接,如果远程连接操作已经开始,但还没有完成,则返回true,否则返回false
// 也就是说,无论底层Socket还没有开始连接,或者已经连接成功,该方法都会返回false
public boolean isConnectionPending()
// 试图完成连接远程服务器的操作
// 非阻塞模式下,建立连接从调用connect()方法开始,到调用finishConnect()方法结束
// 如果在调用此方法之前连接已经建立,则立即返回true,否则立即返回false
// 阻塞模式下,如果连接操作还没有完成,则会进入阻塞状态,直到连接完成,或者出现IO异常
public boolean finishConnect) throws IOException
// 从Channel读入若干字节,存放到参数指定的ByteBuffer
// 假设ByteBuffer剩余容量为r,阻塞模式下,该方法会争取读到r字节
// 如果输入流中不足r字节,就进入阻塞状态,直到读入了r字节,或者读到了输入流末尾,或者出现了IO异常
// 非阻塞模式下,该方法奉行能读到多少数据就读多少数据的原则
// 通道中的可读数据,有可能不足r字节,或者为0字节,总是立即返回
// 该方法返回实际上读入的字节数,有可能为0,如果返回-1,表示读到了输入流的末尾
public int read(ByteBuffer dst) throws IOException
// 把参数src指定的ByteBuffer的字节写到Channel
// 假设ByteBuffer剩余容量为r,阻塞模式下,该方法会争取输出r字节
// 如果底层网络的输出缓冲区不能容纳r字节,就进入阻塞状态,直到输出了r字节,或者出现了IO异常
// 非阻塞模式下,该方法奉行能输出多少数据就输出多少数据的原则,有可能不足r字节,或者为0字节,总是立即返回
// 该方法返回实际上输出的字节,有可能为0
public int write(ByteBuffer src) throws IOException

Selector 类

只要 ServerSockerChannel 以及 SocketChannelSelector 注册了特定的事件,Selector 就会监控这些事件是否发生。SelectableChannelregister() 方法负责注册事件,该方法返回 SelectionKey 对象,该对象是用于跟踪这些被注册事件的句柄

Selector 对象中会包含三种类型的 SelectionKey 的集合:

  • all-keys:当前所有向 Selector 注册的 SelectionKey 的集合,Selectorkeys()
    法返回该集合
  • selected-keys:相关事件已经被 Selector 捕获的 SelectionKey 的集合,Selector
    selectedKeys()方法返回该集合
  • cancelled-keys:已经被取消的 SelectionKey 的集合,Selector 没有提供访问这
    种集合的方法

当执行 SelectableChannelregiste() 方法,会新建一个 SelectionKey 并加入 Selectorall-keys 集合中。如果关闭了与 SelectionKey 对象关联的 Channel 对象,或者调用了 SelectionKey 对象的 cancel() 方法,那么这个 SelectionKey 对象就会被加入 cancelled-keys 集合,表示已经被取消,在程序下一次执行 Selectorselect() 方法时,被取消的 SelectionKey 对象将从所有的集合(包括 all-keys 集合、selected-keys 集合和 cancelled-keys 集合)中被删除

在执行 Selectorselect() 方法时,如果与 SelectionKey 相关的事件发生了,这个 SelectionKey 就被加入 selected-keys 集合中。程序直接调用 selected-keys 集合的 remove() 方法,或者调用它的 Iteratorremove() 方法,都可以从 selected-keys 集合中删除一个 SelectionKey 对象

程序不允许直接通过集合接口的 remove() 方法删除 all-keys 集合中的 SelectionKey 对象,这会导致 UnsupportedOperationException

Selector 类的主要方法如下:

// Selector的静态工厂方法,创建一个Selector对象
public static Selector open() throws IOException
// 判断Selector是否处于打开状态,Selector对象创建后就处于打开状态,当调用close()方法就进入关闭状态
public boolean isOpen()
// 返回Seleclor的all-keys集合,包含了所有与Seclector关联的SelectionKey对象
public Set<SelectionKey> keys()
// 返回相关事件已经发生的SelectionKey对象的数目
// 该方法采用非阻塞的工作方式,返回当前相关事件已经发生的SelectionKey对象的数目,如果没有,就立即返回0
public int selectNow() throws IOException
// 返回相关事件已经发生的SelectionKey对象的数目
// 该方法采用阻塞的工作方式,如果一个也没有,就进入阻塞状态,直到出现以下情况之一,就会从select()返回:
// 1.至少有一个SelectionKey的相关事件已经发生
// 2.其他线程调用了Selector的wakeup()方法
// 3.当前执行select()方法的线程被其他线程中断
// 4.超出了等待时间
public int select() throws IOException
public int select(long timeout) throws IOException
// 唤醒执行Selector的select()方法 
public Selector wakeup()
// 关闭 Selector
// 如果有其他线程正执行这个Selector的select()方法并且处于阻塞状态,这个线程会立即返回
// close()方法使得Selector占用的所有资源都被释败,所有关联的SelectionKey都被取消
public void close() throws IOException

SelectionKey 类

SelectionKey 中定义了四种事件,分别用四个 int 类型的常量来表示:

  • SelectionKey.OP_ACCEPT:接收连接就绪事件,表示服务器监听到了客户连接,服务器可以接收这个连接了,常量值为 16
  • SeiectionKey.OP_CONNECT:连接就绪事件表示客户与服务器的连接已经建立成功,常量值为 8
  • SelectionKey.OP_READ:读就绪事件,表示通道中已经有了可读数据,可以执行读操作了,常量值为 1
  • SelectionKey.OP_WRITE:写就绪事件表示已经可以向通道写数据了,常量值为 4

以上常量分别占据不同的二进制位,因此可以通过二进制的或运算来将它们进行任意组合

一个 SelectionKey 对象中包含两种类型的事件:

  • 所有感兴趣的事件:通过 SelectableChannelregister() 方法注册事件时,可以在参数中指定 SelectionKey 感兴趣的事件

    SelectionKey key = socketChannel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
    

    该代码表示这个 SelectionKey 对读就绪和写就绪事件感兴趣,与之关联的 Selector 对象会负责监控这些事件

    SelectionKey 的带参数的 interestOps(int ops) 方法也可以为 SelectionKey 对象增加一个感兴趣的事件,如下代码所示:

    key.interestOps(SelectionKey.OP_WRITE);
    
  • 所有已经发生的事件:SeletionKeyreadyOps() 方法返回所有已经发生的事件,例如假定返回值为 SelectionKey.OP_WRITE | SelectionKey.OP_READ,表示读就绪和写就绪事件已经发生了,这意味着与之关联的 SocketChannel 对象可以进行读操作和写操作了

SelectionKey 的主要方法如下:

// 返回与这个SelectionKey对象关联的SelectableChannel对象
public SelectableChannel channel()
// 返回与这个SelectionKey对象关联的Selector对象
public Selector selector()
// 判断这个SelectionKey是否有效
// 当SelectionKey对象创建后,它就一直处于有效状态
// 如果调用了它的cancel()方法,或关闭了与它关联的SelectableChannel或Selector对象,它就失效
public boolean isValid()
// 使SelectionKey对象失效
public void cancel()
// 返回这个SelectionKey感兴趣的事件
public int interestOps()
// 为SelectionKey增加感兴趣的事件
public SelectionKey interestOps(int ops)
// 返回已经就绪的事件
public int readyOps()
// 判斯与之关联的SocketChannel的读就绪事件是否已经发生
public final boolean isReadable()
// 判断与之关联的SocketChannel的写就绪事件是否已经发生
public final boolean isWritable()
// 判断与之关联的SocketChannel的连接就绪事件是否已经发生
public final boolean isConnectable()
// 判断与之关联的ServerSocketChannel的接收连接就绪事件是否已经发生
public final boolean isAcceptable()
// 使SelectionKey关联一个附件,一个SelectionKey对象只能关联一个Object类型的附件
// 如果多次调用该方法,则只有最后一个附件与SelectionKey对象关联
public final Object attach(Object obj)
// 返回与SelectionKey对象关联的附件
public final Object attachment()

Channels 类

Channels 类是一个简单的工具类,提供了通道与传统的基于 IO 的流、ReaderWriter 之间进行转换的静态方法

ReadableByteChannel newChannel(InputStream in) // 输入流转换成读通道
WritableByteChannel newChannel(OutputStream out) // 输出流转换成写通道
InputStream newInputStream(AsynchronousByteChannel ch) // 异步通道转换成输入流
InputStream newInputStream(ReadableByteChannel ch) // 读通道转换成输入流
OutputStream newOutputStream(AsynchronousByteChannel ch) // 异步通道转换成输出流
OutputStream newOutputStream(WritableByteChannel ch) // 写通道转换成输出流
Reader newReader(ReadableByteChannel ch,String csName) // 读通道转换成Reader,参数csName指定字符编码
Reader newReader(ReadableByteChannel ch,Charset charset)//读通道转换成Reader.参数charset指定字符编码
Reader newReader(ReadableByteChannel ch,CharsetDecoder dec, int minBufferCap) // 读通道转换成 Reader,参数dec指定字符解码器,参数minBufferCap指定内部字节缓冲区的最小容量
Writer newWriter(WritableByeChannel ch, String csName) // 写通道转换Writer.参数csName指定字符编码
Writer newWriter(WritableByeChannel ch, Charset charset) // / 写通道转换Writer.参数charset指定字符编码
Writer newWriter(WritableByeChannel ch, CharsetEncoder enc, int minBufferCap) // 写通道转换成Writer,参数dec指定字符解码器,参数minBufferCap指定内部字节缓冲区的最小容量

Socket 选项

从 JDK7 开始,SocketChannelServerSocketChannelAsynchronousSocketChannelAsynchronousServerSocketChannelDatagramChannel 都实现了新的 NetworkChannel 接口。NetworkChannel 接口的主要作用是设置和读取各种 Socket 选项

NetworkChannel 接口提供了用于设置和读取这些选项的方法:

<T> T getOption(SocketOption<T> name) // 获取特定的Socket选项值
<T> NetworkChannel setOption(SocketOption<T> name, T value) // 设置特定的Socket选项
Set<SocketOption<?>> supportedOptions() // 获取所有支持的Socket选项

SocketOptionl 类是一个泛型类,SocketOption<T> 中的 T 代表特定选项的取值类型,可选值包括 IntegerBooleanNetworkInterface

StandardSocketOptions 类提供了以下表示特定选项的常量:文章来源地址https://www.toymoban.com/news/detail-443971.html

SocketOption<NetworkInterface>  --  StandardSocketOptions.IP_MULTICAST_IF
SocketOption<Boolean>  --  StandardSocketOptions.IP_MULTICAST_LOOP
SocketOption<Integer>  --  StandardSocketOptions.IP_MULTICAST_TTL
SocketOption<Integer>  --  StandardSocketOptions.IP_TOS
SocketOption<Boolean>  --  StandardSocketOptions.SO_BROADCAST
SocketOption<Boolean>  --  StandardSocketOptions.SO_KEEPALIVE
SocketOption<Integer>  --  StandardSocketOptions.SO_LINGER
SocketOption<Integer>  --  StandardSocketOptions.SO_RCVBUF
SocketOption<Boolean>  --  StandardSocketOptions.SO_REUSEADDR
SocketOption<Boolean>  --  StandardSocketOptions.SO_REUSEPORT
SocketOption<Integer>  --  StandardSocketOptions.SO_SNDBUF
SocketOption<Boolean>  --  StandardSocketOptions.TCP_NODELAY

到了这里,关于Java 网络编程 —— 非阻塞式编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java 网络编程 —— 创建多线程服务器

    一个典型的单线程服务器示例如下: 服务端接收到一个客户连接,就与客户进行通信,通信完毕后断开连接,然后接收下一个客户连接,假如同时有多个客户连接请求这些客户就必须排队等候。如果长时间让客户等待,就会使网站失去信誉,从而降低访问量。 一般用并发性

    2024年02月02日
    浏览(46)
  • 突破编程_C++_网络编程(TCPIP 四层模型(概述))

    TCP/IP 协议,全称为 Transmission Control Protocol/Internet Protocol,中文名为传输控制协议/因特网互联协议,又名网络通讯协议。这是 Internet 最基本的协议,也是 Internet 国际互联网络的基础。它主要由网络层的 IP 协议和传输层的 TCP 协议组成,定义了电子设备如何连入因特网,以及数

    2024年04月08日
    浏览(45)
  • Linux C程序开发,多线程编程、网络编程

    目录 多线程编程 网络编程 Linux C程序开发是指在Linux操作系统下使用C语言进行开发的过程。Linux是一种开源的操作系统,具有稳定性、安全性和灵活性等优点,因此在很多领域都得到了广泛的应用。 多线程编程是指在一个程序中同时运行多个线程,每个线程都有自己的执行路

    2024年02月13日
    浏览(43)
  • 《3.linux应用编程和网络编程-第8部分-3.8.网络基础》 3.8.1.网络通信概述 3.8.3.网络通信基础知识2

        进程间通信: 管道 、 信号量、 共享内存, 技术多,操作麻烦     线程就是解决 进程间 通信 麻烦的事情,这是线程的 优势 3.8.1.网络通信概述 3.8.1.1、从进程间通信说起: 网络域套接字socket , 网络通信其实就是位于网络中不同主机上面                   的 

    2024年02月15日
    浏览(53)
  • 多线程|多进程|高并发网络编程

    多进程并发服务器是一种经典的服务器架构,它通过创建多个子进程来处理客户端连接,从而实现并发处理多个客户端请求的能力。 概念: 服务器启动时,创建主进程,并绑定监听端口。 当有客户端连接请求时,主进程接受连接,并创建一个子进程来处理该客户端连接。

    2024年02月07日
    浏览(36)
  • 【Linux网络编程】高并发服务器框架 线程池介绍+线程池封装

    前言 一、线程池介绍 💻线程池基本概念 💻线程池组成部分 💻线程池工作原理  二、线程池代码封装 🌈main.cpp 🌈ThreadPool.h 🌈ThreadPool.cpp 🌈ChildTask.h  🌈ChildTask.cpp 🌈BaseTask.h 🌈BaseTask.cpp 三、测试效果 四、总结 📌创建线程池的好处 本文主要学习 Linux内核编程 ,结合

    2024年01月16日
    浏览(91)
  • Linux网络编程:多进程 多线程_并发服务器

    文章目录: 一:wrap常用函数封装 wrap.h  wrap.c server.c封装实现 client.c封装实现 二:多进程process并发服务器 server.c服务器 实现思路 代码逻辑  client.c客户端 三:多线程thread并发服务器 server.c服务器 实现思路 代码逻辑  client.c客户端 ​​​​   read 函数的返回值 wrap.h  wrap

    2024年02月12日
    浏览(54)
  • [Linux] 网络编程 - 初见TCP套接字编程: 实现简单的单进程、多进程、多线程、线程池tcp服务器

    网络的上一篇文章, 我们介绍了网络变成的一些重要的概念, 以及 UDP套接字的编程演示. 还实现了一个简单更简陋的UDP公共聊天室. [Linux] 网络编程 - 初见UDP套接字编程: 网络编程部分相关概念、TCP、UDP协议基本特点、网络字节序、socket接口使用、简单的UDP网络及聊天室实现…

    2024年02月16日
    浏览(64)
  • 探索Java并发编程利器:LockSupport,一种高效的线程阻塞与唤醒机制

    关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 我们继续总结学习 Java基础知识 ,温故知新。 LockSupport 是 Java SE 9 及以上版本中引入的一个线程同步工具类,用

    2024年02月16日
    浏览(49)
  • 计算机网络编程 | 并发服务器代码实现(多进程/多线程)

    欢迎关注博主 Mindtechnist 或加入【Linux C/C++/Python社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。 专栏:《网络编程》 当涉及到构建高性能的服务

    2024年02月08日
    浏览(68)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包