NIO.2特性总结(二)增强的通道 NetworkChannel

半世苍凉 提交于 2019-12-07 20:57:29

接上一篇<NIO.2特性总结(一)灵活的Path>

想了想还是先把NIO.2 socket部分先看了,毕竟这部分对我更有用,而且昨儿看了目录,对于文件系统的操作部分总体还比较简单,所以在后面再补上,今天先记点儿有关NIO2 socket部分。

在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的继承关系:

//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多了哪些操作:

客户端的代码我就不写了,至于那十多个操作,你可以看API,或者去看书,书上也有很详细的代码示例,我这么做的目的就是提个注意,技术更新很快,好多老的写法如果有更好的写法可以更新。

然后我们看一下非阻塞的,庆幸的是selector这个最重要的元素在java7中没有变化。而且实现也没有大变化:

代码都可以从《pro java nio.2》中找到,都是很基本的代码。和NIO1没有太大变化最后我们看下基于UDP的操作。DatagramChannel同样实现了NetworkChannel,也可以setsocket operations,而且DatagramChannel在open时可以选择IP协议版本:

/**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可以查看本地所有网卡的信息,是否支持多播等:

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了,真正的异步才将开始。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!