Java 非阻塞I/O使用方法
绝大部分知识与实例来自o'reilly的《java网络编程》(java network programming,fourth edition,by elliotte rusty harold(o'reilly))。
非阻塞i/o简介
非阻塞i/o(nio)是处理高并发的一种手段。在高并发的情况下,创建和回收线程以及在线程间切换的开销变得不容忽视,此时就可以使用非阻塞i/o技术。这种技术的核心思想是每次选取一个准备好的连接,尽快地填充这个连接所能管理的尽可能多的数据,然后转向下一个准备好的连接。
利用非阻塞i/o实现的客户端
一般情况下,客户端不会需要处理很高数量的并发连接。事实上,非阻塞i/o主要是为服务器设计的,但它也可以用在客户端上。由于客户端的设计相比服务器容易,因此下面先用客户端来进行简单演示。
首先介绍通道(channel)和缓冲区。非阻塞i/o中使用socketchannel类创建连接。要获取socketchannel对象,需要将一个socketaddress对象(通常会使用它的子类inetsocketaddress)传入它的静态工厂方法open()中。下面为一个示例:
socketaddress address = new inetsocketaddress("127.0.0.1", 19); socketchannel client = socketchannel.open(address);
open()方法是阻塞的,因此这之后的代码在连接建立之前不会执行。如果连接无法建立,会抛出一个ioexception异常。
连接建立之后就需要获取输入和输出。不同于传统的getinputstream()与getoutputstream(),利用通道,你可以直接写入通道本身。不是写入字节数组,而是要写入一个bytebuffer对象。bytebuffer对象通过bytebuffer.allocate(int capacity)获取,capacity为缓冲区大小,单位为字节:
bytebuffer buffer = bytebuffer.allocate(74);
获得bytebuffer对象后,将其传递给socketchannel对象的read()方法,socketchannel对象会用从socket读取的数据填充这个缓冲区。read()方法返回成功读取并储存在缓冲区中的字节数。默认情况下,它会至少读取一个字节,或者返回-1指示数据结束,没有字节可用时阻塞。这与inputstream的行为大致相同。但如果设置成非阻塞模式,没有字节可用时它会立即返回0,不会阻塞。
现在假定缓冲区内已经有了一些数据,之后就需要将它们提取出来。可以使用传统的方式,先将数据写入一个字节数组,之后再写入一个输出流中。这里介绍一种完全基于通道的方法:利用channels工具类将输出流封装到一个通道中:
writablebytechannel out = channels.newchannel(system.out);
上面的代码将system.out封装入一个通道中。这之后就可以进行输出了。bytebuffer对象在每次输出之前,需要调用一下它的flip()方法,使得通道从开头开始读。在读写完毕后,还需要调用它的clear()方法,重置缓冲区的状态。下面是进行一次数据输出的代码:
buffer.flip(); out.write(buffer); buffer.clear();
实例1:利用非阻塞i/o实现的chargenerator(字符生成器)客户端
服务器代码:
public static void createchargeneratorserver(){ try(serversocket server = new serversocket(19)){ while(true){ try(socket connection = server.accept()){ outputstream out = connection.getoutputstream(); int firstprintablecharacter = 33; int numberofprintablecharacter = 94; int numberofcharactersperline = 72; int start = firstprintablecharacter; while(true){ for(int i = start ; i < start + numberofcharactersperline ; i++){ out.write (firstprintablecharacter + (i - firstprintablecharacter) % numberofprintablecharacter); } out.write('\r'); out.write('\n'); start = firstprintablecharacter + (start + 1 - firstprintablecharacter) % numberofprintablecharacter; } }catch (ioexception e) { e.printstacktrace(); } } } catch (ioexception e) { e.printstacktrace(); } }
客户端代码:
try { socketaddress address = new inetsocketaddress("127.0.0.1", 19); socketchannel client = socketchannel.open(address); bytebuffer buffer = bytebuffer.allocate(74); writablebytechannel out = channels.newchannel(system.out); while(client.read(buffer) != -1){ buffer.flip(); out.write(buffer); buffer.clear(); } } catch (ioexception e) { e.printstacktrace(); } 输出(无限循环): ]^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@abcdef ^_`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@abcdefg _`abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@abcdefgh `abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@abcdefghi abcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@abcdefghij bcdefghijklmnopqrstuvwxyz{|}~!"#$%&'()*+,-./0123456789:;<=>?@abcdefghijk
启用非阻塞模式
上面的程序和使用输入/输出流的传统方式并没有太大差别。不过,可以调用serversocket的configureblocking(false)方法将其设置为非阻塞模式。这个模式下,如果没有可用的数据,read()方法会立即返回,这让客户端可以去做其他事情。不过,由于read()方法在读不到数据时会返回0,读取数据的循环需要做一些改动:
while(true){ //这里可以写每次循环都要做的事,无论有没有读到数据 int n = client.read(buffer); if(n > 0){ buffer.flip(); out.write(buffer); buffer.clear(); }else if (n == -1) { //除非服务器故障,否则不会发生 break; } }
总结
以上就是本文关于java 非阻塞i/o使用方法的全部内容,希望对大家有所帮助。欢迎各位参阅:java网络编程基础篇之单向通信, java使用代理进行网络连接方法示例等,有什么问题可以随时留言,小编会及时回复大家。感谢大家对本站的支持!
上一篇: mysql自增ID起始值修改方法