NIO.2特性總結(二)加強的通道 NetworkChannel

接上一篇<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的操做。下面咱們看具體的。先總結一下NIO2channel上的加強。首先是新增的接口NetworkChannel,咱們先看jdk6jdk1.7SocketChannel的繼承關係: 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個方法,前兩個bindgetLocalAddress咱們都熟悉,新增的三個方法都是爲了提供對socket的操做(socket operations),這些操做由SocketOption接口定義,由StandardSocketOperation作具體實現。SocketOption接口定義了操做須要實現的nameoperationStandardSocketOperation被放在了java.net包下,而不是在NIO的包下。這裏大體有10多個標準操做,如IP_MULTICAST_IFIP_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);

另外在java7DatagramChannel能夠實現多播,經過繼承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.         NetworkChannelSocketOptionStandardSocketOperation的加入讓咱們能夠對channel控制的更細膩了。

2.         非阻塞中的selector沒有變化,這方面能夠放心的使用。

3.         基於UDPchannel增長了對Ip版本的選擇,經過NetworkInterface能夠對本地網卡作詳細的檢索,並實現了MulticastChannel,更方便的實現多播。

關於channel的加強就寫到這兒了,下篇就要寫AIO了,真正的異步纔將開始。

相關文章
相關標籤/搜索