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的输出流可以写数据)向输出流写数据; // 非阻塞}
以上处理流程采用了轮询的工作方式,当某一种操作就绪,就执行该操作,否则就查看是否还有其他就绪的操作可以执行。线程不会因为某一个操作还没有就绪,就进入阻塞状态,一直傻傻地在那里等待这个操作就绪
为了使轮询的工作方式顺利进行,接收客户的连接、从输入流读数据,以及向输出流写数据的操作都应该以非阻寒的方式运行。所谓非阻塞,指当线程执行这些方法时,如果操作还没有就绪,就立即返回,而不会一直等到操作就绪
非阻塞通信 APIjava.nio.channels
包提供了支持非阻塞通信的类,如下所述:
ServerSocketChannel
:ServerSocket
的替代类,支持阻塞通信与非阻塞通信SocketChannel
:Socket
的替代类,支持阻塞通信与非阻塞通信Selector
:为 ServerSocketChannel
监控接收连接就绪事件,为 SocketChannel
监控连接就绪、读就绪和写就绪事件SelectionKey
:代表 ServerSocketChannel
以及 SocketChannel
向 Selector
注册事件的句柄。当一个 SelectionKey
对象位于 Selector
对象的 selected-keys
集合中,就表示与这个 SelectionKey
对象相关的事件发生了ServerSocketChannel
及 SocketChannel
都是 SelectableChannel
的子类,如图所示,SelectableChannel
类及其子类都能委托 Selector
来监控它们可能发生的一些事件,这种委托过程也被称为注册事件过程
ServerSocketChannel
向 Selector
注册接收连接就绪事件的代码如下:
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)
:接收数据,把它们存放到参数指定的 ByteBufferwrite(ByteBuffer buffer)
:把参数指定的 ByteBuffer 中的数据发送出去ByteBuffer
表示字节缓冲区,SocketChannel
的 read()
和 write()
方法都会操纵 ByteBuffer
。ByteBuffer
类继承于 Buffer
类。ByteBuffer
中存放的是字节,为了把它们转换为字符串还需要用到 Charset
类,Charset
类代表字符编码,它提供了把字节流转换为字符串(解码过程)和把字符串转换为字节流(编码过程)的实用方法
下面分别介绍 Buffer
、Charset
、SelectableChannel
、ServerSocketChannel
、SocketChannel
、Selector
和 SelectionKey
的用法
数据输入和输出往往是比较耗时的操作,缓冲区从两个方面提高 I/O 操作的效率:
减少实际的物理读写次数缓冲区在创建时被分配内存,这块内存区域一直被重用,这可以减少动态分配和回收内存区域的次数java.nio
包公开了 Buffer
类的 API,使得 Java 程序可以直接控制和运用缓冲区,所有的缓冲区都有以下属性:
以上三个属性的关系为:容量 > 极限 >= 位置 >= 0
缓冲区提供了用于改变以上三个属性的方法:
// 把极限设为容量,把位置设为0clear();// 把极限设为位置,把位置设为 0flip();// 不改变极限,把位置设为0rewind();
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 类型以外,每种基本类型都有对应的缓冲区类,包括 CharBuffer
,DoubleBuffer
,FloatBuffer
,IntBuffer
,LongBuffer
,ShortBuffer
。在 CharBuffer
中存放的数据单元为字符,以此类推。还有一种缓冲区是 MappedByteBuffer
,它是 ByteBuffer
的子类,能够把缓冲区和文件的某个区域直接映射
所有具体缓冲区类都提供了读写缓冲区的方法:
// 相对读,从缓冲区的当前位置读取一个单元的数据,读完后把位置加1get();// 绝对读,从参数 index 指定的位置读取一个单元的数据get(int index);// 相对写,向缓冲区的当前位置写一个单元的数据,写完后把位置加1put(单元数据类型 data);// 绝对写,向参数index指定的位置写入一个单元的数据put(int index, 单元数据类型 data);
ByteBuffer
类不仅可以读取和写入一个单元的字节,还可以读取和写入 int、char、float 和 double 等基本类型的数据,例如:
getInt()getInt(int index)
以上不带 index 参数的方法会在当前位置读取或写入数据,称为相对读写。带 index 参数的方法会在 index 参数指定的位置读取或写入数据,称为绝对读写
字符编码 Charsetjava.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 的主要层次结构如下:
java.nio.channels.Channel
接口只声明了两个方法:
// 关闭通道close();// 判断通道是否打开isOpen();
Channel
接口的两个最重要的子接口是 ReadableByteChannel
和 WritableByteChannel
。ReadableByteChannel
接口声明了 read(ByteBuffer dst)
方法,该方法把数据源的数据读入参数指定的 ByteBuffer
缓冲区中。WritableByteChannel
接口声明了 write(ByteBuffer src)
方法,该方法把参数指定的 ByteBuffer
缓冲区中的数据写到数据汇中
ByteChannel
接口是一个便利接口,它扩展了 ReadableByteChannel
和 WritableByteChannel
接口,因而同时支持读写操作
ScatteringByteChannel
接口扩展了 ReadableByteChannel
接口,允许分散地读取数据。分散读取数据指单个读取操作能填充多个缓冲区,ScatteringByteChannel
接口声明了 read(ByteBuffer[] dsts)
方法,该方法把从数据源读取的数据依次填充到参数指定的各个 ByteBuffer
GatheringByteChannel
扩展了 WritableByteChannel
接口,允许集中地写入数据。集中写入数据指单个写操作能把多个缓冲区的数据写到数据, GatheringByteChannel
接口声明了 write(ByteBuffer[] srcs)
方法,该方法依次把参数指定的每个 ByteBuffer
中的数写到数据汇
FileChannel
类是 Channel
接口的实现类,代表一个与文件相连的通道。该类实现了 ByteChannel
、ScatteringByteChannel
和 GatheringByteChannel
接口,支持读操作、写操作、分散读操作和集中写操作。FileChannel
类没有提供公开的构造方法,因此不能用 new
语句来构造它的实例。不过,在FileInputStream
、FileOutputStream
和 RandomAccessFile
类中提供了 getChannel()
方法,该方法返回相应的 FileChannel
对象
SelectableChannel
也是一种通道,它不仅支持阻塞的 I/O
操作,还支持非阻塞的 I/O
。SelectableChannel
有两个子类,ServerSocketChannel
和 SocketChannel
。SocketChannel
还实现了 ByteChannel
接口,具有 read(ByteBuffer dst)
和 write(ByteBuffer src)
方法
SelectableChannel
是一种支持阻塞 IO 和非阻塞 IO 的通道。在非阻塞模式下,读写数据不会阻塞,并且 SelectableChannel
可以向 Selector
注册读就绪和写就绪等事件。Selector
负责监控这些事件,等到事件发生时,比如发生了读就绪事件,SelectableChannel
就可以执行读操作了
SelectableChannel
的主要方法如下:
// 当参数block为true,表示把SelectableChannel设为阻塞模式// 当参数block为false时,表示把SelectableChannel设为非阻塞模式// SelectableChannel默认采用阻塞模式// 该方法返回SelectableChannel对象本身的引用,相当于return thispublic SelectableChannel configureBlocking(boolean block) throws IOException// 以下两个方法都向Selector注册事件public SelectionKey register(Selector sel,int ops) throws ClosedChannelExceptionpublic SelectionKey register(Selector sel,int ops,Object attachment) throws ClosedChannelException
以下是 socketChannel
向 Selector
注册读就绪和写就绪事件
SelectionKey key = socketChannel.register(selector.SelectionKey.OP_READ | SelectionKey.OP_WRITE);
register()
方法返回一个 SelectionKey
对象,SeletionKey
被用来跟踪被注册的事件。第二个 register()
方法还有一个 Object
类型的参数 attachment
,用于为 SelectionKey
关联附件,当被注册事件发生后,需要处理该事件时,可以从 SelectionKey
中获得这个附件,该附件可用来包含与处理这个事件相关的信息
ServerSocketChannel
继承自 SelectableChannel
,是 ServerSocket
的替代类,通过它的静态方法 open()
来创建。每个 ServerSockeChannel
对象都与一个 ServerSocket
对象关,通过 socket()
方法返回与它关联的 ServerSocket
对象。可通过以下方式把服务器进程绑定到一个本地端口:
serverSocketChannel.socket().bind(port);
ServerSocketChannel
的主要方法如下:
// 返回一个ServerSocketChannel对象,该对象没有与任何本地端口绑定,并且处于阻塞模式public static ServerSocketChannel open() throws IOException// 用于接收客户的连接,如果处于非阻塞状态,当没有客户连接时就立即返回nullpublic SocketChannel accept() throws IOException// 返回ServerSocketChannel所能产生的事件,这个方法总是返回SelectionKey.OP_ACCEPTpublic final int validOps()// 返回ServerSocketChannel关联的ServerSocket对象public ServerSocket socket()
3. SocketChannel类SockeChannel
可以被看作是 Socket
的替代类,SockeChannel
不仅继承了 SelectableChannel
,而且实现了 ByteChannel
。SockeChannel
同样通过它的静态方法 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_WRITEpublic final int validOps()// 返回SocketChannel关联的Socket对象public Socket socket()// 建立远程连接,当处于非阻塞模式时,如果立即连接成功返回true,否则返回falsepublic boolean connect(SocketAddress remote) throws IOException// 判断底层Socket是否已经建立远程连接public boolean isConnected()// 判断是否正在进行远程连接,如果远程连接操作已经开始,但还没有完成,则返回true,否则返回false// 也就是说,无论底层Socket还没有开始连接,或者已经连接成功,该方法都会返回falsepublic 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字节,总是立即返回// 该方法返回实际上输出的字节,有可能为0public int write(ByteBuffer src) throws IOException
Selector 类只要 ServerSockerChannel
以及 SocketChannel
向 Selector
注册了特定的事件,Selector
就会监控这些事件是否发生。SelectableChannel
的 register()
方法负责注册事件,该方法返回 SelectionKey
对象,该对象是用于跟踪这些被注册事件的句柄
Selector
对象中会包含三种类型的 SelectionKey
的集合:
all-keys
:当前所有向 Selector
注册的 SelectionKey
的集合,Selector
的 keys()
方法返回该集合selected-keys
:相关事件已经被 Selector
捕获的 SelectionKey
的集合,Selector
的 selectedKeys()
方法返回该集合cancelled-keys
:已经被取消的 SelectionKey
的集合,Selector
没有提供访问这种集合的方法当执行 SelectableChannel
的 registe()
方法,会新建一个 SelectionKey
并加入 Selector
的 all-keys
集合中。如果关闭了与 SelectionKey
对象关联的 Channel
对象,或者调用了 SelectionKey
对象的 cancel()
方法,那么这个 SelectionKey
对象就会被加入 cancelled-keys
集合,表示已经被取消,在程序下一次执行 Selector
的 select()
方法时,被取消的 SelectionKey
对象将从所有的集合(包括 all-keys
集合、selected-keys
集合和 cancelled-keys
集合)中被删除
在执行 Selector
的 select()
方法时,如果与 SelectionKey
相关的事件发生了,这个 SelectionKey
就被加入 selected-keys
集合中。程序直接调用 selected-keys
集合的 remove()
方法,或者调用它的 Iterator
的 remove()
方法,都可以从 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 keys()// 返回相关事件已经发生的SelectionKey对象的数目// 该方法采用非阻塞的工作方式,返回当前相关事件已经发生的SelectionKey对象的数目,如果没有,就立即返回0public int selectNow() throws IOException// 返回相关事件已经发生的SelectionKey对象的数目// 该方法采用阻塞的工作方式,如果一个也没有,就进入阻塞状态,直到出现以下情况之一,就会从select()返回:// 1.至少有一个SelectionKey的相关事件已经发生// 2.其他线程调用了Selector的wakeup()方法// 3.当前执行select()方法的线程被其他线程中断// 4.超出了等待时间public int select() throws IOExceptionpublic 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
:接收连接就绪事件,表示服务器监听到了客户连接,服务器可以接收这个连接了,常量值为 16SeiectionKey.OP_CONNECT
:连接就绪事件表示客户与服务器的连接已经建立成功,常量值为 8SelectionKey.OP_READ
:读就绪事件,表示通道中已经有了可读数据,可以执行读操作了,常量值为 1SelectionKey.OP_WRITE
:写就绪事件表示已经可以向通道写数据了,常量值为 4以上常量分别占据不同的二进制位,因此可以通过二进制的或运算来将它们进行任意组合
一个 SelectionKey
对象中包含两种类型的事件:
所有感兴趣的事件:通过 SelectableChannel
的 register()
方法注册事件时,可以在参数中指定 SelectionKey
感兴趣的事件
SelectionKey key = socketChannel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
该代码表示这个 SelectionKey
对读就绪和写就绪事件感兴趣,与之关联的 Selector
对象会负责监控这些事件
SelectionKey
的带参数的 interestOps(int ops)
方法也可以为 SelectionKey
对象增加一个感兴趣的事件,如下代码所示:
key.interestOps(SelectionKey.OP_WRITE);
所有已经发生的事件:SeletionKey
的 readyOps()
方法返回所有已经发生的事件,例如假定返回值为 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 的流、Reader
和 Writer
之间进行转换的静态方法
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 开始,SocketChannel
、ServerSocketChannel
、AsynchronousSocketChannel
、AsynchronousServerSocketChannel
和 DatagramChannel
都实现了新的 NetworkChannel
接口。NetworkChannel
接口的主要作用是设置和读取各种 Socket 选项
NetworkChannel
接口提供了用于设置和读取这些选项的方法:
T getOption(SocketOption name) // 获取特定的Socket选项值 NetworkChannel setOption(SocketOption name, T value) // 设置特定的Socket选项Set> supportedOptions() // 获取所有支持的Socket选项
SocketOptionl
类是一个泛型类,SocketOption
中的 T
代表特定选项的取值类型,可选值包括 Integer
、Boolean
和 NetworkInterface
StandardSocketOptions
类提供了以下表示特定选项的常量:
SocketOption -- StandardSocketOptions.IP_MULTICAST_IFSocketOption -- StandardSocketOptions.IP_MULTICAST_LOOPSocketOption -- StandardSocketOptions.IP_MULTICAST_TTLSocketOption -- StandardSocketOptions.IP_TOSSocketOption -- StandardSocketOptions.SO_BROADCASTSocketOption -- StandardSocketOptions.SO_KEEPALIVESocketOption -- StandardSocketOptions.SO_LINGERSocketOption -- StandardSocketOptions.SO_RCVBUFSocketOption -- StandardSocketOptions.SO_REUSEADDRSocketOption -- StandardSocketOptions.SO_REUSEPORTSocketOption -- StandardSocketOptions.SO_SNDBUFSocketOption -- StandardSocketOptions.TCP_NODELAY
关键词:
相关阅读
-
Java 网络编程 —— 非阻塞式编程
线程阻塞概述在生活中,最常见的阻塞现象是公路上汽车的堵塞。汽车... -
湖南裕能:碳酸锂价格向合理区间回归有...
5月14日电,湖南裕能近日接受机构调研时表示,今年一季度行业受碳酸... -
oppo手机怎么root权限不用电脑 oppo手...
今天来聊聊关于oppo手机怎么root权限不用电脑,oppo手机怎么ROOT的... -
韩国总统办公室:尹锡悦将在日本广岛同...
韩国总统办公室5月14日宣布,韩国总统尹锡悦将出席下周在日本广岛举... -
网友发现洪欣的致歉信不是繁体字,而且...
洪欣的致歉信不是繁体字 -
跑起来了!无锡锡澄S1线列车正式上线-当...
日前,无锡首条市域城际轨道交通线锡澄S1线开启热滑实验,这标志着... -
世界即时:大众ID.7 GTX预告图发布,将...
5月14日消息,大众ID 7GTX发布预告图,新车是纯电动中型三厢轿车ID... -
环球关注:特斯拉召回超110万辆车,是因...
特斯拉又启动召回了,这次召回的规模特别大,达到了110多万台。而且... -
恒大汽车剥离地产业务 聚焦新能源赛道
5月12日,恒大汽车(0708 HK)发布公告称,股东大会已同意向中国恒大... -
环球看点!新闻采编与制作排名院校 ...
今天,大学路小编为大家带来了新闻采编与*排名院校院校专业:,希望... -
小手指发麻是什么病的前兆_起水痘的前兆...
1、水痘患儿一般没有前驱症状,大多直接进入出疹阶段。成年患者可能... -
特斯拉OTA 动能回收制动强度选择和深踩...
说实话,如果只看“召回、1104622辆”这两个关键词,那确实够吓人的! -
避开福特通用,巴菲特语出惊人,它们是...
给你机会你不中用!连巴菲特都看不上,合资品牌中,谁能赢得机会? -
精彩看点:风雨欲来! 最新一批新车目...
目前像比亚迪宋ProDM-i、宋PLUSDM-i、枭龙MAX、深蓝S7、银河L7等一... -
潍柴动力与比亚迪签署战略合作协议 世...
5月12日上午,潍柴动力与比亚迪在深圳签署战略合作协议,潍柴动力董... -
沃尔沃EX90量产推迟至2024年上半年 世界视讯
5月14日,有媒体报道称,沃尔沃EX90量产时间从原定于今年上半年更改... -
水利部等11部门:建立协调联动机制 发...
人民网北京5月14日电(欧阳易佳)近日,水利部、中央精神文明建设办... -
angelababy快乐大本营2011(angelababy快乐大本营)
1、有的。2、9月26期和蔡徐坤一起参加了。以上就是【angelababy快乐... -
房子进深是指什么(房屋进深是什么意思...
1、习惯上,我们把一个楼(或房间)的主要采光面称为开间(或面宽)... -
零成本召回110万辆汽车,特斯拉终于认错...
超过110万辆汽车召回,创造电动智能汽车历史上单次最大规模召回记录...