想了想还是先把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的操作。下面我们看具体的。先总结一下NIO2对channel上的增强。首先是新增的接口NetworkChannel,我们先看jdk6和jdk1.7中SocketChannel的继承关系:
//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多了哪些操作:
客户端的代码我就不写了,至于那十多个操作,你可以看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);
另外在java7中DatagramChannel可以实现多播,通过继承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. NetworkChannel、SocketOption和StandardSocketOperation的加入让我们可以对channel控制的更细腻了。
2. 非阻塞中的selector没有变化,这方面可以放心的使用。
3. 基于UDP的channel增加了对Ip版本的选择,通过NetworkInterface可以对本地网卡做详细的检索,并实现了MulticastChannel,更方便的实现多播。
关于channel的增强就写到这儿了,下篇就要写AIO了,真正的异步才将开始。
来源:oschina
链接:https://my.oschina.net/u/589742/blog/93418