当前位置: 首页 > news >正文

苏州网站建设姜超学习网站建设课程

苏州网站建设姜超,学习网站建设课程,墨客网站建设xcyxqc,物业公司网站模板对于用ServerSocket以及Socket编写的服务器程序和客户程序#xff0c;它们在运行过程中常常会阻塞。例如当一个线程执行ServerSocket的accept()方法时#xff0c;假如没有客户连接#xff0c;该线程就会一直等到有了客户连接才从accept()方法返回。再例如当线程执行Socket的…对于用ServerSocket以及Socket编写的服务器程序和客户程序它们在运行过程中常常会阻塞。例如当一个线程执行ServerSocket的accept()方法时假如没有客户连接该线程就会一直等到有了客户连接才从accept()方法返回。再例如当线程执行Socket的输入流的read()方法时如果输入流中没有数据该线程就会一直等到读入了足够的数据才从read()方法返回。 假如服务器程序需要同时与多个客户通信就必须分配多个工作线程让它们分别负责与某个客户通信当然每个工作线程都有可能经常处于长时间的阻塞状态。 从JDK1.4版本开始引入了非阻塞的通信机制。服务器程序接收客户连接、客户程序请求建立与服务器的连接以及服务器程序和客户程序收发数据的操作都可以按非阻塞的方式进行。服务器程序只需要创建一个线程就能完成同时与多个客户通信的任务。 非阻塞的通信机制主要由java.nio包新I/O包中的类实现主要的类包括ServerSocketChannel、SocketChannel、Selector、SelectionKey和ByteBuffer等。 1、线程阻塞的概念 在生活中最常见的阻塞现象是公路上汽车的堵塞。汽车在公路上快速行驶如果前方交通受阻就只好停下来等待等到公路顺畅才能恢复行驶。 线程在运行中也会因为某些原因而阻塞。所有处于阻塞状态的线程的共同特征是放弃CPU暂停运行只有等到导致阻塞的原因消除才能恢复运行或者被其他线程中断该线程会退出阻塞状态并且抛出InterruptedException。 1.1、线程阻塞的原因 导致线程阻塞的原因主要有以下方面 线程执行了Thread.sleep(int n)方法线程放弃CPU睡眠n ms然后恢复运行。线程要执行一段同步代码由于无法获得相关的同步锁只好进入阻塞状态等到获得了同步锁才能恢复运行。线程执行了一个对象的wait()方法进入阻塞状态只有等到其他线程执行了该对象的notify()或notifyAll()方法才可能将其唤醒。线程执行I/O操作或进行远程通信时会因为等待相关的资源而进入阻塞状态。例如当线程执行System.in.read()方法时如果用户没有向控制台输入数据则该线程会一直等读到了用户的输入数据才从read()方法返回。 进行远程通信时在客户程序中线程在以下情况下可能进入阻塞状态 请求与服务器建立连接时即当线程执行Socket的带参数的构造方法或执行Socket的connect()方法时会进入阻塞状态直到连接成功此线程才从Socket的构造方法或connect()方法返回。线程从Socket的输入流读入数据时如果没有足够的数据就会进入阻塞状态直到读到了足够的数据或者到达输入流的末尾或者出现了异常才从输入流的read()方法返回或异常中断。输入流中有多少数据才算足够呢这要看线程执行的read()方法的类。1int read()只要输入流中有1字节就算足够。2int read(byte[] buff)只要输入流中的字节数目与参数buff数组的长度相同就算足够。3String readLine()只要输入流中有1行字符串就算足够。值得注意的是InputStream类并没有readLine()方法在过滤流BufferedReader类中才有此方法。线程向Socket的输出流写一批数据时可能会进入阻塞状态等到输出了所有的数据或者出现异常才从输出流的write()方法返回或异常中断。如果调用Socket的setSoLinger()方法设置了关闭Socket的延迟时间那么当线程执行Socket的close()方法时会进入阻塞状态直到底层Socket发送完所有剩余数据或者超过了setSoLinger()方法设置的延迟时间才从close()方法返回。 在服务器程序中线程在以下情况下可能会进入阻塞状态 线程执行ServerSocket的accept()方法等待客户的连接直到接收到了客户连接才从accept()方法返回。线程从Socket的输入流读入数据时,如果输入流没有足够的数据就会进入阻塞状态。线程向Socket的输出流写一批数据时可能会进入阻塞状态等到输出了所有的数据或者出现异常才从输出流的write()方法返回或异常中断。 由此可见无论是在服务器程序还是客户程序中当通过Socket的输入流和输出流来读写数据时都可能进入阻塞状态。这种可能出现阻塞的输入和输出操作被称为阻塞I/O。与此对照如果执行输入和输出操作时不会发生阻塞则称为非阻塞I/O。 1.2、服务器程序用多线程处理阻塞通信的局限 创建多线程的服务器已经介绍了服务器程序用多线程来同时处理多个客户连接的方式。服务器程序的处理流程如下图所示。 主线程负责接收客户的连接。在线程池中有若干工作线程它们负责处理具体的客户连接。每当主线程接收一个客户连接主线程就会把与这个客户交互的任务交给一个空闲的工作线程去完成主线程继续负责接收下一个客户连接。 在上图中用粗体框标识的步骤为可能引起阻塞的步骤。从图中可以看出当主线程接收客户连接以及工作线程执行I/O操作时都有可能进入阻塞状态。 服务器程序用多线程来处理阻塞I/O尽管能满足同时响应多个客户请求的需求但是有以下局限 1Java虚拟机会为每个线程都分配独立的堆栈空间工作线程数目越多系统开销就越大而且增加了Java虚拟机调度线程的负担增加了线程之间同步的复杂性提高了线程死锁的可能性。 2工作线程的许多时间都浪费在阻塞I/O操作上Java虚拟机需要频繁地转让CPU的使用权使进入阻塞状态的线程放弃CPU再把CPU分配给处于可运行状态的线程。 由此可见工作线程并不是越多越好。如下图所示保持适量的工作线程会提高服务器的并发性能但是当工作线程的数目到达某个极限超出了系统的负荷时反而会降低并发性能使得多数客户无法快速得到服务器的响应。 1.3、非阻塞通信的基本思想 假如同时要做两件事烧开水和煮粥。烧开水的步骤如下 煮粥的步骤如下 为了同时完成两件事一种方案是同时请两个人分别做其中的一件事这相当于采用多线程来同时完成多个任务。还有一种方案是让一个人同时完成两件事这个人应该善于利用一件事的空闲时间去做另一件事这个人一刻也不应该闲着。为了同时完成两件事一种方案是同时请两个人分别做其中的一件事这相当于采用多线程来同时完成多个任务。还有一种方案是让一个人同时完成两件事这个人应该善于利用一件事的空闲时间去做另一件事这个人一刻也不应该闲着。 这个人不断监控烧水和煮粥的状态如果发生了“水烧开”“粥煮开”或“粥煮熟”事件就去处理这些事件处理完一件事后继续监控烧水和煮粥的状态直到所有的任务都完成。 以上工作方式也可以被运用到服务器程序中服务器程序只需要一个线程就能同时接收客户的连接、接收各个客户发送的数据以及向各个客户发送响应数据。服务器程序的处理流程如下 以上处理流程采用了轮询的工作方式当某一种操作就绪就执行该操作否则就查看是否还有其他就绪的操作可以执行。线程不会因为某一个操作还没有就绪就进入阻塞状态一直傻傻地在那里等待这个操作就绪。 为了使轮询的工作方式顺利进行接收客户的连接、从输入流读数据以及向输出流写数据的操作都应该以非阻塞的方式运行。所谓非阻塞指当线程执行这些方法时如果操作还没有就绪就立即返回而不会一直等到操作就绪。例如当线程接收客户连接时如果没有客户连接就立即返回再例如当线程从输入流中读数据时如果输入流中还没有数据就立即返回或者如果输入流还没有足够的数据那么就读取现有的数据然后返回。值得注意的是以上while循环条件中的操作还是按照阻塞方式进行的如果未发生任何事件就会进入阻塞状态直到接收连接就绪事件、读就绪事件或写就绪事件中至少有一个事件发生此时才会执行while循环体中的操作。 2、非阻塞通信API的用法 java.nio.channels包提供了支持非阻塞通信的类如下所述 ServerSocketChannelServerSocket的替代类支持阻塞通信与非阻塞通信。SocketChannelSocket的替代类支持阻塞通信与非阻塞通信。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类的一些静态常量表示事件类型ServerSocketChannel只可能发生一种事件: SelectionKey.OP_ACCEPT接收连接就绪事件表示至少有了一个客户连接服务器可以接收这个连接。 SocketChannel可能发生以下3种事件。 SelectionKey.OP_CONNECT连接就绪事件表示客户与服务器的连接已经建立成功。SelectionKey.OP_READ读就绪事件表示输入流中已经有了可读数据可以执行读操作了。SelectionKey.OP_WRITE写就绪事件表示已经可以向输出流写数据了。 SocketChannel提供了接收和发送数据的方法。 read(ByteBuffer buffer)接收数据把它们存放到参数指定的ByteBuffer中。write(ByteBuffer buffer)把参数指定的ByteBuffer中的数据发送出去。 ByteBuffer表示字节缓冲区SocketChannel的read()和write()方法都会操纵ByteBuffer。ByteBuffer类继承于Buffer类。ByteBuffer中存放的是字节为了把它们转换为字符串还需要用到Charset类Charset类代表字符编码它提供了把字节流转换为字符串解码过程和把字符串转换为字节流编码过程的实用方法。 ByteBuffer表示字节缓冲区SocketChannel的read()和write()方法都会操纵ByteBuffer。ByteBuffer类继承于Buffer类。ByteBuffer中存放的是字节为了把它们转换为字符串还需要用到Charset类Charset类代表字符编码它提供了把字节流转换为字符串解码过程和把字符串转换为字节流编码过程的实用方法。 2.1、缓冲区 数据输入和输出往往是比较耗时的操作。缓冲区Buffer从两个方面提高I/O操作的效率 减少实际的物理读写次数。缓冲区在创建时被分配内存这块内存区域一直被重用这可以减少动态分配和回收内存区域的次数。 旧I/O类库对应java.io包中的BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter在其实现中都运用了缓冲区。java.nio包公开了Buffer API使得Java程序可以直接控制和运用缓冲区。下图显示了Buffer类的层次结构 所有的缓冲区都有以下属性 容量capacity表示缓冲区可以保存多少数据。极限limit表示缓冲区的当前终点不能对缓冲区中超过极限的区域进行读写操作。极限是可以被修改的这有利于缓冲区的重用。例如假定容量为100的缓冲区已经填满了数据接着程序在重用缓冲区时仅仅将10个新的数据写入缓冲区中从位置0到10的区域这时可以将极限设为10这样就不能读取位置从11到99的原先的数据了。极限是一个非负整数不应该大于容量。位置position表示缓冲区中下一个读写单元的位置每次读写缓冲区的数据时该值都会改变为下一次读写数据做准备。位置是一个非负整数不应该大于极限。 如下图所示以上3个属性的关系为容量极限位置0 缓冲区提供了用于改变以上3个属性的方法 clear()把极限设为容量把位置设为0。flip()把极限设为位置把位置设为0。rewind()不改变极限把位置设为0。 Buffer类的remaining()方法返回缓冲区的剩余容量取值等于极限-位置。Buffer类的compact()方法删除缓冲区内从0到当前位置position的内容然后把从当前位置position到极限limit的内容拷贝到0到limit-position的区域内当前位置position和极限limit的取值也做相应的变化如下图所示 java.nio.Buffer类是一个抽象类不能被实例化。它共有8个具体的缓冲区类其中最基本的缓冲区是ByteBuffer它存放的数据单元是字节。ByteBuffer类并没有提供公开的构造方法但是提供了两个获得ByteBuffer实例的静态工厂方法 allocate(int capacity)返回一个ByteBuffer对象参数capacity指定缓冲区的容量。directAllocate(int capacity):返回一个ByteBuffer对象参数capacity指定缓冲区的容量。该方法返回的缓冲区被称为直接缓冲区它与当前操作系统能够更好地耦合因此能进一步提高I/O操作的速度。但是分配直接缓冲区的系统开销很大因此只有在缓冲区较大并且长期存在或者需要经常重用时才使用这种缓冲区。 除boolean类型以外每种基本类型都有对应的缓冲区类包括CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer和ShortBuffer。这几个缓冲区类都有一个能够返回自身实例的静态工厂方法allocate(int capacity)。在CharBuffer中存放的数据单元为字符在DoubleBuffer中存放的数据单元为double数据以此类推。还有一种缓冲区是MappedByteBuffer它是ByteBuffer的子类。MappedByteBuffer能够把缓冲区和文件的某个区域直接映射。 所有具体缓冲区类都提供了读写缓冲区的方法 get()相对读。从缓冲区的当前位置读取一个单元的数据读完后把位置加1。get(int index)绝对读。从参数index指定的位置读取一个单元的数据。put(单元数据类型 data)相对写。向缓冲区的当前位置写入一个单元的数据写完后把位置加1。put(int index单元数据类型 data)绝对写。向参数index指定的位置写入一个单元的数据。 ByteBuffer类不仅可以读取和写入一个单元的字节还可以读取和写入int、char、float和double等基本类型的数据例如 getInt()getInt(int index)putInt(int value)putInt(int index,int value)getChar()·getChar(int index)putChar(char value)·putChar(int index,char value) 以上不带index参数的方法会在当前位置读取或写入数据称为相对读写。带index参数的方法会在index参数指定的位置读取或写入数据称为绝对读写。 ByteBuffer类还提供了用于获得缓冲区视图的方法例如 ShortBuffer asShortBuffer()CharBuffer asCharBuffer()IntBuffer asIntBuffer()FloatBuffer asFloatBuffer() 例如以下程序代码获取ByteBuffer的CharBuffer缓冲区视图 CharBuffer charBuffer byteBuffer.asCharBuffer();以上CharBuffer视图和底层ByteBuffer共享同样的数据修改CharBuffer视图的数据会反映到底层ByteBuffer。不过CharBuffer视图和底层ByteBuffer有各自独立的位置position、极限limit和容量capacity属性。 2.2、字符编码Charset java.nio.Charset类的每个实例代表特定的字符编码类型。如下图所示 把字节序列转换为字符串的过程称为解码把字符串转换为字节序列的过程称为编码。 Charset类提供了编码与解码的方法 ByteBuffer encode(String str)对参数str指定的字符串进行编码把得到的字节序列存放在一个ByteBuffer对象中并将其返回。ByteBuffer encode(CharBuffer cb)对参数cb指定的字符缓冲区中的字符进行编码把得到的字节序列存放在一个ByteBuffer对象中并将其返回。CharBuffer decode(ByteBuffer bb)对参数bb指定的ByteBuffer中的字节序列进行解码把得到的字符序列存放在一个CharBuffer对象中并将其返回。 Charset类的静态forName(String encode)方法返回一个Charset对象它代表参数encode指定的编码类型。例如以下代码创建了一个代表“GBK”编码的Charset对象 Charset charset Charset.forName(GBK);Charset类还有一个静态方法defaultCharset()它返回代表本地平台的默认字符编码的Charset对象。 2.3、通道 通道Channel用来连接缓冲区与数据源或数据汇即数据目的地。如下图所示数据源的数据经过通道到达缓冲区缓冲区的数据经过通道到达数据汇。 下图展示了通道的主要层次结构 java.nio.channels.Channel接口只声明了两个方法 close()关闭通道。isOpen()判断通道是否打开。 通道在创建时被打开一旦关闭通道就不能重新打开它。 Channel接口的两个最重要的子接口是ReadableByteChannel和WritableByteChannel。ReadableByteChannel接口声明了read(ByteBuffer dst)方法该方法把数据源的数据读入参数指定的ByteBuffer缓冲区中。WritableByteChannel接口声明了write(ByteBuffer src)方法该方法把参数指定的ByteBuffer缓冲区中的数据写到数据汇中。下图展示了Channel与Buffer的关系。ByteChannel接口是一个便利接口它扩展了ReadableByteChannel和WritableByteChannel接口因而同时支持读写操作 ScatteringByteChannel接口扩展了ReadableByteChannel接口允许分散地读取数据。分散读取数据指单个读取操作能填充多个缓冲区。ScatteringByteChannel接口声明了read(ByteBuffer[] dsts)方法该方法把从数据源读取的数据依次填充到参数指定的ByteBuffer数组的各个ByteBuffer中。GatheringByteChannel接口扩展了WritableByteChannel接口允许集中地写入数据。集中写入数据指单个写操作能把多个缓冲区的数据写到数据汇。GatheringByteChannel接口声明了write(ByteBuffer[] srcs)方法该方法依次把参数指定的ByteBuffer数组的每个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)方法。 2.4、SelectableChannel类 SelectableChannel是一种支持阻塞I/O和非阻塞I/O的通道。在非阻塞模式下读写数据不会阻塞并且SelectableChannel可以向Selector注册读就绪和写就绪等事件。Selector负责监控这些事件等到事件发生时比如发生了读就绪事件SelectableChannel就可以执行读操作了。 SelectableChannel的主要方法如下 public SelectableChannel configureBlocking(boolean block) throws IOException当参数block为true时表示把 SelectableChannel设为阻塞模式当参数block为false时表示把 SelectableChannel设为非阻塞模式。在默认情况下SelectableChannel采用阻塞模式。该方法返回SelectableChannel对象本身的引用相当于“return this”。SelectableChannel的isBlocking()方法判断SelectableChannel是否处于阻塞模式如果返回true则表示处于阻塞模式否则表示处于非阻塞模式。public SelectionKey register(Selector sel,int ops)throws ClosedChannelExceptionpublic SelectionKey register(Selector sel,int ops,Object attachment)throws ClosedChannelException 以上两个方法都向Selector注册事件例如以下socketChannelSelectableChannel的一个子类向Selector注册读就绪和写就绪事件 SelectionKey key socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);register()方法返回一个SelectionKey对象SelectionKey被用来跟踪被注册的事件。第2个register()方法还有一个Object类型的参数attachment它被用于为SelectionKey关联一个附件当被注册事件发生后需要处理该事件时可以从SelectionKey中获得这个附件该附件可用来包含与处理这个事件相关的信息。以下这两段代码是等价的。 2.5、ServerSocketChannel类 ServerSocketChannel从SelectableChannel中继承了configureBlocking()和register()方法。ServerSocketChannel是ServerSocket的替代类也具有负责接收客户连接的accept()方法。ServerSocketChannel并没有public类型的构造方法必须通过它的静态方法open()来创建ServerSocketChannel对象。每个ServerSocketChannel对象都与一个ServerSocket对象关联。ServerSocketChannel的socket()方法返回与它关联的ServerSocket对象。可通过以下方式把服务器进程绑定到一个本地端口 serverSocketChannel.socket().bind(port);ServerSocketChannel的主要方法如下 public static ServerSocketChannel open()throws IOException这是ServerSocketChannel类的静态工厂方法它返回一个ServerSocketChannel对象。这个对象没有与任何本地端口绑定并且处于阻塞模式。public SocketChannel accept()throws IOException类似于ServerSocket的accept()方法用于接收客户的连接。如果ServerSocketChannel处于非阻塞模式当没有客户连接时该方法就立即返回null。如果ServerSocketChannel处于阻塞模式当没有客户连接时它就会一直阻塞下去直到有客户连接就绪或者出现了IOException。值得注意的是该方法返回的SocketChannel对象处于阻塞模式如果希望把它改为非阻塞模式就必须执行以下代码socketChannel.configureBlocking(false);public final int validOps()返回ServerSocketChannel所能产生的事件这个方法总是返回SelectionKey.OP_ACCEPT。public ServerSocket socket()返回与ServerSocketChannel关联的ServerSocket对象。每个ServerSocketChannel对象都与一个ServerSocket对象关联。 2.6、SocketChannel类 SocketChannel可以被看作是Socket的替代类但它比Socket具有更多的功能。SocketChannel不仅从SelectableChannel父类中继承了configureBlocking()和register()方法而且实现了ByteChannel接口因此具有用于读写数据的read(ByteBuffer dst)和write(ByteBuffer src)方法。SocketChannel没有public类型的构造方法必须通过它的静态方法open()来创建SocketChannel对象。 SocketChannel的主要方法如下 public static SocketChannel open() throws IOExceptionpublic static SocketChannel open(SocketAddress remote)throws IOException SocketChannel的静态工厂方法open()负责创建SocketChannel对象第2个带参数的构造方法还会建立与远程服务器的连接。在阻塞模式以及非阻塞模式下第2个open()方法有不同的行为这与SocketChannel类的connect()方法类似。 以下两段代码是等价的 值得注意的是open()方法返回的SocketChannel对象处于阻塞模式如果希望把它改为非阻塞模式就必须执行以下代码 socketChannel.configureBlocking(false);public final int validOps()返回SocketChannel所能产生的事件这个方法总是返回以下值SelectionKey.OP_CONNECT|SelectionKey.OP_READ | SelectionKey.OP_WRITEpublic Socket socket()返回与这个SocketChannel关联的Socket对象。每个SocketChannel对象都与一个Socket对象关联。public boolean isConnected()判断底层Socket是否已经建立了远程连接。public boolean isConnectionPending()判断是否正在进行远程连接。如果远程连接操作已经开始但还没有完成则返回true否则返回false。也就是说无论底层Socket还没有开始连接或者已经连接成功该方法都会返回false。public boolean connect(SocketAddress remote)throws IOException使底层Socket建立远程连接。当SocketChannel处于非阻塞模式时如果立即连接成功则该方法返回true如果不能立即连接成功则该方法返回false程序稍后必须通过调用finishConnect()方法来完成连接。当SocketChannel处于阻塞模式时如果立即连接成功则该方法返回true如果不能立即连接成功则进入阻塞状态直到连接成功或者出现I/O异常。public boolean finishConnect()throws IOException试图完成连接远程服务器的操作。在非阻塞模式下建立连接从调用SocketChannel的connect()方法开始到调用finishConnect()方法结束。如果finishConnect()方法顺利完成连接或者在调用此方法之前连接已经被建立则finishConnect()方法立即返回true。如果连接操作还没有完成则立即返回false。如果连接操作中遇到异常而失败则抛出相应的I/O异常。在阻塞模式下如果连接操作还没有完成则会进入阻塞状态直到连接完成或者出现I/O异常。public int read(ByteBuffer dst)throws IOException从Channel中读入若干字节把它们存放到参数指定的ByteBuffer中。假定执行read()方法前ByteBuffer的位置为p剩余容量为rr等于dst.remaining()方法的返回值。假定read()方法实际上读入了n字节那么0≤n≤r。当read()方法返回后参数dst引用的ByteBuffer的位置变为pn极限保持不变如下图所示 在阻塞模式下read()方法会争取读到r字节如果输入流中不足r字节就进入阻塞状态直到读入了r字节或者读到了输入流末尾或者出现了I/O异常。 在非阻塞模式下read()方法奉行能读到多少数据就读多少数据的原则。read()方法读取当前通道中的可读数据有可能不足r字节或者为0字节read()方法总是立即返回而不会等到读取了r字节再返回。 read()方法返回实际上读入的字节数有可能为0。如果返回“-1”就表示读到了输入流的末尾。 把参数src指定的ByteBuffer中的字节写到Channel中。假定执行write()方法前ByteBuffer的位置为p剩余容量为rr等于src.remaining()方法的返回值。假定write()方法实际上向通道中写了n字节那么0≤n≤r。当write()方法返回后参数src引用的ByteBuffer的位置变为pn极限保持不变如下图所示 在阻塞模式下write()方法会争取输出r字节如果底层网络的输出缓冲区不能容纳r字节就进入阻塞状态直到输出了r字节或者出现了I/O异常。 在非阻塞模式下write()方法奉行能输出多少数据就输出多少数据的原则有可能不足r字节或者为0字节write()方法总是立即返回而不会等到输出r字节再返回。 write()方法返回实际上输出的字节数有可能为0。 2.7、Selector类 只要ServerSocketChannel以及SocketChannel向Selector注册了特定的事件Selector就会监控这些事件是否发生。SelectableChannel的register()方法负责注册事件该方法返回一个SelectionKey对象该对象是用于跟踪这些被注册事件的句柄。一个Selector对象中会包含3种类型的SelectionKey的集合。 2.8、SelectionKey类 ServerSocketChannel或SocketChannel通过register()方法向Selector注册事件时register()方法会创建一个SelectionKey对象这个SelectionKey对象用来跟踪注册事件的句柄。在SelectionKey对象的有效期间Selector会一直监控与SelectionKey对象相关的事件如果事件发生就会把SelectionKey对象加入selected-keys集合中。在以下情况下SelectionKey对象会失效这意味着Selector再也不会监控与它相关的事件。 1程序调用SelectionKey的cancel()方法。 2关闭与SelectionKey关联的Channel。 3与SelectionKey关联的Selector被关闭。 2.9、 Channels类 Channels类是一个简单的工具类提供了通道与传统的基于I/O的流、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(WritableByteChannel ch,String csName)写通道转换成Writer。参数csName指定字符编码。Writer newWriter(WritableByteChannel ch,Charset charset)写通道转换成Writer。参数charset指定字符编码。Writer newWriter(WritableByteChannel ch,CharsetEncoder enc,int minBufferCap)写通道转换成Writer。参数enc指定字符编码器。参数minBufferCap指定内部字节缓冲区的最小容量。 以上方法的参数包括ReadableByteChannel、WritableByteChannel和AsynchronousByteChannel类型。SocketChannel实现了ReadableByteChannel和WritableByteChannel接口AsynchronousSocketChannel实现了AsynchronousByteChannel接口。 以下程序代码把SocketChannel转换成输入流接下来就能按照输入流的方式来读取数据 2.10、Socket选项 从JDK7开始SocketChannel、ServerSocketChannel、AsynchronousSocketChannel、AsynchronousServerSocketChannel和DatagramChannel都实现了新的NetworkChannel接口。NetworkChannel接口的主要作用是设置和读取各种Socket选项例如TCP_NODELAY、SO_LINGER、SO_SNDBUF和SO_RCVBUF等。 3、异步通道和异步运算结果 从JDK7开始引入了表示异步通道的AsynchronousSocketChannel类和AsynchronousServerSocketChannel类这两个类的作用与SocketChannel类和ServerSocketChannel相似区别在于异步通道的一些方法总是采用非阻塞模式并且它们的非阻塞方法会立即返回一个Future对象用来存放方法的异步运算结果。 AsynchronousSocketChannel类有以下非阻塞方法 Future connect(SocketAddress remote)连接远程主机。Future read(ByteBuffer dst)从通道中读入数据存放到ByteBuffer中。Future对象中包含了实际从通道中读到的字节数。Future write(ByteBuffer src)把ByteBuffer中的数据写入通道中。Future对象中包含了实际写入通道的字节数。AsynchronousServerSocketChannel类有以下非阻塞方法。·Futureaccept()接受客户连接请求。Future对象中包含了连接建立成功后创建的AsynchronousSocketChannel对象。
http://www.pierceye.com/news/386337/

相关文章:

  • 自己做的网站访问不了建站如何挣钱
  • 网盘做网站做网站推广员工
  • 河北正规网站建设比较网页制作平台哪家好
  • 2017网站seo如何做wordpress设置登录背景
  • 网站的模块怎么做网站建设的技术支持包括
  • 青岛网站设计哪家好游戏小程序开发定制
  • 建设网站西丽提升网站建设品质
  • 大良营销网站建设方案广东东莞划定多个高风险区
  • 毕业设计做网站用什么品牌网站建设新闻
  • c 网站开发用的人多吗做it行业招标网站有哪些
  • 招聘做牙技工的网站用html框架做网站
  • 本地wordpress站点上传央企网站群建设
  • 广州免费自助建站开发wordpress公园
  • 淘宝客建网站简单网站建设优化推广
  • 长沙做网站找哪家好毕业设计开题报告网站开发
  • 可以写代码的网站有哪些问题吗网页设计与网站建设期末考试试卷
  • 美工做网站怎么收费网站设计规范
  • 建网站需要注意的问题企业整合营销
  • 2018网站开发的革新wordpress 更新数据库
  • 做现金贷的网站有哪些如何自己建立一个网站
  • 网站制作公司前十名wordpress保存的字体大小
  • 网站设计思路方案海外购物网站哪个最好
  • 哪个网站做课件能赚钱青岛制作网站的
  • 深圳做网站排名公司哪家好html网页设计实训报告范文
  • 外贸网站开发做动画 的 网站有哪些软件下载
  • 中国建设银行吉林省分行官网站wordpress形式
  • 做门户网站可以用的字体黑龙江建设教育网站
  • 怎么做网站建设赚钱知名品牌设计logo解析
  • wordpress全站静态化做公司网站 找谁做
  • 广安网站建设哪家好网站模板下载网站