接上一篇<NIO.2特性总结(一)灵活的Path> java
想了想仍是先把NIO.2的 socket部分先看了,毕竟这部分对我更有用,并且昨儿看了目录,对于文件系统的操做部分整体还比较简单,因此在后面再补上,今天先记点儿有关NIO2 socket部分。 api
在NIO1中,咱们主要用到的有这个三类channel:ServerSocketChannel,SocketChannel和DatagramChannel,以及NIO的核心选择器Selecotr。经过这些类的组合就能写出高性能的、非阻塞IO程序,NIO1强调的是非阻塞Non-blocking,固然非阻塞并不单单指selector这样的轮询能够一下处理好多请求,这部分的内容能够看我博客的相关文章或者直接看《Reilly - Java NIO》。而NIO.2提出的是异步,异步加非阻塞,就是咱们熟悉的AIO。虽然NIO1也能经过其余办法实现异步,好比mina中利用将操做分离,用队列并伴随wait和notify来实现异步,但NIO2给咱们提供直接的接口来实现异步,会下降不少开发难度。仍是要再提一下异步和非阻塞是没有什么关系的,书上有一段话很好: 网络
固然NIO.2也增强了NIO1中对channel的操做。下面咱们看具体的。先总结一下NIO2对channel上的加强。首先是新增的接口NetworkChannel,咱们先看jdk6和jdk1.7中SocketChannel的继承关系: app
//jdk6 public abstract class SocketChannel extends AbstractSelectableChannel implements ByteChannel, ScatteringByteChannel, GatheringByteChannel //1.7 public abstract class SocketChannel extends AbstractSelectableChannel implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel
不只是SocketChannel,上面提到的三个channel都实现了NetworkChannel接口。NetworkChannel中有5个方法,前两个bind和getLocalAddress咱们都熟悉,新增的三个方法都是为了提供对socket的操做(socket operations),这些操做由SocketOption接口定义,由StandardSocketOperation作具体实现。SocketOption接口定义了操做须要实现的name和operation。StandardSocketOperation被放在了java.net包下,而不是在NIO的包下。这里大体有10多个标准操做,如IP_MULTICAST_IF、IP_MULTICAST_LOOP等,这些均可以经过SocketOption中定义的set/get方法来设置或获得。看一个阻塞的实现,程序没什么功能,就是列一下用法: 异步
package com.a2.nio2.chapter8.socketapis; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketOption; import java.net.StandardSocketOptions; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Set; public class BlockTcpServer { public void init() throws IOException { /**try block-with-resource*/ try (ServerSocketChannel ssc = ServerSocketChannel.open();) { ssc.configureBlocking(true); /** setting the ops,固然你不设置,这里就是默认的操做,和NIO1同样 */ ssc.setOption(StandardSocketOptions.SO_RCVBUF, 4 * 1024); ssc.setOption(StandardSocketOptions.SO_REUSEADDR, true); /** iterator the ops in ssc */ Set<SocketOption<?>> options = ssc.supportedOptions(); for (SocketOption<?> option : options) System.out.println(option); /** jdk1.6的时候,ServerSocketChannel是不恩bind的,必须让SocketChannel去bind */ ssc.bind(new InetSocketAddress(2181)); SocketChannel channel = ssc.accept(); System.out.println("accept:" + channel.getRemoteAddress()); ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (channel.read(buffer) != -1) { buffer.flip(); channel.write(buffer); if (buffer.hasRemaining()) { buffer.compact(); } else { buffer.clear(); } } /** since 1.7 */ channel.shutdownInput(); channel.shutdownOutput(); ssc.close(); channel.close(); } catch (Exception e) { // TODO: handle exception } } public static void main(String[] args) throws IOException { BlockTcpServer bts = new BlockTcpServer(); bts.init(); } }
其实这里有不少和jdk6不同的写法了,注释里都写出了,特别是bind的操做,咱们看看1.7以后ServerSocketChannel多了哪些操做: socket
客户端的代码我就不写了,至于那十多个操做,你能够看API,或者去看书,书上也有很详细的代码示例,我这么作的目的就是提个注意,技术更新很快,好多老的写法若是有更好的写法能够更新。 ide
而后咱们看一下非阻塞的,庆幸的是selector这个最重要的元素在java7中没有变化。并且实现也没有大变化: 性能
代码均可以从《pro java nio.2》中找到,都是很基本的代码。和NIO1没有太大变化最后咱们看下基于UDP的操做。DatagramChannel一样实现了NetworkChannel,也能够setsocket operations,并且DatagramChannel在open时能够选择IP协议版本: spa
/**choose the ipv4*/ DatagramChannel datagramChannel=DatagramChannel.open(StandardProtocolFamily.INET); datagramChannel.setOption(StandardSocketOptions.SO_RCVBUF, 4 * 1024); datagramChannel.setOption(StandardSocketOptions.SO_SNDBUF, 4 * 1024);
另外在java7中DatagramChannel能够实现多播,经过继承MulticastChannel来实现。NetworkInterface能够查看本地全部网卡的信息,是否支持多播等: .net
package com.a2.nio2.chapter8.socketapis; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; /** * * This application will return all the network interfaces found on your * machine, and for each one will render its display name (a human-readable * String describing the network device) and name (the real name used to * identify a network interface). Moreover, each network interface is checked to * see if it supports multicast, if it is virtual (a subinterface), and if it is * up and running. * */ public class TestNetworkInterface { public static void main(String argv[]) throws Exception { Enumeration<NetworkInterface> enumInterfaces = NetworkInterface .getNetworkInterfaces(); while (enumInterfaces.hasMoreElements()) { NetworkInterface net = (NetworkInterface) enumInterfaces .nextElement(); System.out.println("Network Interface Display Name: " + net.getDisplayName()); System.out.println(net.getDisplayName() + " is up and running ?" + net.isUp()); System.out.println(net.getDisplayName() + " Supports Multicast: " + net.supportsMulticast()); System.out .println(net.getDisplayName() + " Name: " + net.getName()); System.out.println(net.getDisplayName() + " Is Virtual: " + net.isVirtual()); System.out.println("IP addresses:"); Enumeration<InetAddress> enumIP = net.getInetAddresses(); while (enumIP.hasMoreElements()) { InetAddress ip = (InetAddress) enumIP.nextElement(); System.out.println("IP address:" + ip); } } } }
这样就能够找到网卡,而后根据name来组织你的代码了:
NetworkInterface networkInterface = NetworkInterface.getByName("eth3");
看一下多播时候的代码,这里要注意有个Group,多播组播都是有特殊的地址的:
datagramChannel.send(datetime, new InetSocketAddress(InetAddress.getByName(GROUP), DEFAULT_PORT));
NIO2增长了MembershipKey,在客户端支持选择性的加入多播组:
MembershipKey key = datagramChannel.join(group,networkInterface);
这个key有点儿相似selector中的key,也有不一样的选项。其余的操做和普通IO没什么区别,就再也不赘述了,若是你对这些都没有什么概念,那先去看看网络的书,了解多播的概念,而后再去《pro》中看看具体的实现代码,都很简单的示例代码,因此这里就不贴了。
总结一下,channel的加强主要有那么几方面:
1. NetworkChannel、SocketOption和StandardSocketOperation的加入让咱们能够对channel控制的更细腻了。
2. 非阻塞中的selector没有变化,这方面能够放心的使用。
3. 基于UDP的channel增长了对Ip版本的选择,经过NetworkInterface能够对本地网卡作详细的检索,并实现了MulticastChannel,更方便的实现多播。
关于channel的加强就写到这儿了,下篇就要写AIO了,真正的异步才将开始。