阻塞IO
网络IO提供了阻塞和非阻塞两种模式,首先我们来看一下阻塞IO的代码示例。
服务端:
try {
//打开服务端通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//绑定地址端口号
ssc.bind(new InetSocketAddress(7890));
//循环接收
while(true) {
//获得连接的客户端通道
SocketChannel sc = ssc.accept();
//初始化ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读数据到buffer
int size = sc.read(buffer);
if(size > 0){
//控制台打印一下
System.out.println(new String(buffer.array()));
}
/*写个回复
buffer.clear();
buffer.put("回复一下".getBytes());
buffer.flip();
sc.write(buffer);*/
//关闭客户端通道
sc.close();
}
} catch (IOException e) {
e.printStackTrace();
}
客户端:
try {
//打开客户端通道
SocketChannel sc = SocketChannel.open();
//连接服务端
sc.connect(new InetSocketAddress("127.0.0.1",7890));
//初始化byteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//写入信息
byteBuffer.put(msg.getBytes());
//转换成读模式
byteBuffer.flip();
//将byteBuffer的信息写入通道
sc.write(byteBuffer);
/*读取回复
byteBuffer.clear();
sc.read(byteBuffer);
System.out.println(new String(byteBuffer.array()));*/
//关闭通道
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
现在运行服务端,再运行客户端,这样服务端就能打印出客户端发送的消息,整体来讲与Java的Socket网络编程类似。接下来看一下非阻塞IO。
非阻塞IO,NIO模型
如果使用非阻塞方式,就需要引入选择器Selector,用来轮询查找就绪的事件。流程如下:Channel实例化,设置为非阻塞,然后将对象及监听的事件注册到Selector中,然后通过Selector的轮询获取就绪的事件,找出就绪事件后执行对应的下一步操作。这个整体的流程就是前文中的IO多路复用模式。
下面是服务端的代码示例:
try {
//打开选择器
Selector selector = Selector.open();
//打开服务端通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//绑定地址端口号
ssc.bind(new InetSocketAddress(7890));
//设置为非阻塞模式
ssc.configureBlocking(false);
//将服务端通道注册到选择器中,监听事件为接收就绪
ssc.register(selector, SelectionKey.OP_ACCEPT);
//选择器找出就绪的事件
while (selector.select() > 0) {
//获取就绪事件集合的迭代器
Iterator<SelectionKey> selectionKeyIterator
= selector.selectedKeys().iterator();
while (selectionKeyIterator.hasNext()) {
SelectionKey selectionKey = selectionKeyIterator.next();
if (selectionKey.isAcceptable()) {
//获得连接的客户端通道
SocketChannel sc = ssc.accept();
//设置为非阻塞模式
sc.configureBlocking(false);
//将服务端通道注册到选择器中,监听事件为读就绪
sc.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
SocketChannel sc = (SocketChannel) selectionKey.channel();
//初始化ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读数据到buffer
int size = sc.read(buffer);
if (size > 0) {
//控制台打印一下
System.out.println(new String(buffer.array()));
}
//关闭客户端通道
sc.close();
}
selectionKeyIterator.remove();
}
}
ssc.close();
} catch (IOException e) {
e.printStackTrace();
}
客户端可以采用阻塞是传输,不影响服务端接收信息。也可以用非阻塞式,代码需要稍作调整,增加一段循环等待就绪的代码,示例如下:
//设置为阻塞方式
sc.configureBlocking(false);
//连接服务端
sc.connect(new InetSocketAddress("127.0.0.1", 7890));
//空循环,等待就绪
while (!sc.finishConnect()) {
}
//下面沿用上文的写数据流程
至此SocketChannel的基本操作,还有Selector的基本用法都介绍完了,对Java的NIO实现已经有了一个初步认识。后面我们介绍一下Java为我们提供UDP协议网路传输NIO类——DatagramChannel。