接上一篇<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了,真正的異步纔將開始。