Java.nio全称java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。目前很多通信都是基于Nio来实现。下面分享一个入门的案例。
服务端的代码如下:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;
public class NioServer {
public static void main(String[] args) throws IOException {
NioServer nioServer = new NioServer();
nioServer.start();
}
/**
* 启动服务器
* @throws IOException
*/
public void start() throws IOException {
//1创建一个Selector
Selector selector = Selector.open();
//2通过ServerSocketChannel创建channel通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//3为channel通道绑定端口
serverSocketChannel.bind(new InetSocketAddress(8010));
//4设置channel为非阻塞模式
serverSocketChannel.configureBlocking(false);
//5讲channel注册到selector上,监听链接事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器启动成功...");
//6循环,等待新进入的链接
while(true){
//是一个阻塞方法,只有监听事件就绪了才会返回,
/**
* TODO 获取可用的channel数量
*/
int readyChannels = selector.select();
if(readyChannels == 0) {
continue;
}
/**
* 获取可用Channel的集合
*/
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterable = selectionKeys.iterator();
while(iterable.hasNext()) {
/**
* selectionKey实例
*/
SelectionKey selectionKey = iterable.next();
/**
* 移除当前的selectionKey
*/
iterable.remove();
//7根据就绪状态调用对应方法处理业务逻辑
/**
* 如果是接入事件
*/
if(selectionKey.isAcceptable()) {
acceptHandler(serverSocketChannel,selector);
}
/**
* 如果是可读事件
*/
if(selectionKey.isReadable()) {
readHandler(selectionKey, selector);
}
}
}
}
/**
* 接入事件处理
* @throws IOException
*/
private void acceptHandler(ServerSocketChannel serverSocketChannel,Selector selector) throws IOException {
/**
* 如果是接入事件,创建socketChannel
*/
SocketChannel socketChannel = serverSocketChannel.accept();
/**
* 将socketChannel设置为非阻塞工作模式
*/
socketChannel.configureBlocking(false);
/**
* 讲channel注册到selector上,监听可读事件
*/
socketChannel.register(selector, SelectionKey.OP_READ);
/**
* 回复客户端提示信息
*/
socketChannel.write(Charset.forName("UTF-8").encode("您连接成功了"));
}
/**
* 可读事件处理
* @throws IOException
*/
private void readHandler(SelectionKey selectionKey,Selector selector) throws IOException {
/**
* 要从selectionKey中获取到已经就绪的channel
*/
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
/**
* 创建buffer
*/
ByteBuffer buffer = ByteBuffer.allocate(1024);
/**
* 将channel再次注册到selector上,监听他的可读事件
*/
String request = "";
while(socketChannel.read(buffer) > 1) {
/**
* 切换为读模式
*/
buffer.flip();
/**
* 读取buffer中的内容
*/
request += Charset.forName("UTF-8").decode(buffer);
}
/**
* 将客户端发送的请求信息,广播给所有其他客户端
*/
broadCast(selector,socketChannel,request);
socketChannel.register(selector, SelectionKey.OP_READ);
if(request.length() > 0) {
System.out.println(request);
}
}
/**
* 广播
* @throws IOException
*/
private void broadCast(Selector selector,SocketChannel sourceChannel,String request) throws IOException {
/**
* 获取所有已经接入的客户端channel
*/
Set<SelectionKey> selectionKeys = selector.keys();
Iterator<SelectionKey> it = selectionKeys.iterator();
/**
* 循环向所有channel发送数据
*/
while(it.hasNext()) {
SelectionKey selectionKey = it.next();
Channel channel = selectionKey.channel();
//剔除发消息的客户端
if(channel instanceof SocketChannel && channel != sourceChannel) {
((SocketChannel)channel).write(Charset.forName("UTF-8").encode(request));
}
}
}
}
客户端的代码如下:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
public class NioClient {
public static void main(String[] args) throws IOException {
/**
* 连接服务器
*/
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost",8010));
System.out.println("客户端启动成功...");
/**
* 接收服务器端的响应数据
* 新开一个线程
*/
Selector selector = Selector.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
new Thread(new NioClientHandler(selector)).start();
/**
* 向服务端发送数据
*/
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()) {
String request = scanner.nextLine();
if(request != null && request.length() > 0) {
socketChannel.write(Charset.forName("UTF-8").encode(request));
}
}
}
}
class NioClientHandler implements Runnable{
private Selector selector;
public NioClientHandler(Selector selector) {
this.selector = selector;
}
/**
* 可读事件处理
* @throws IOException
*/
private void readHandler(SelectionKey selectionKey,Selector selector) throws IOException {
/**
* 要从selectionKey中获取到已经就绪的channel
*/
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
/**
* 创建buffer
*/
ByteBuffer buffer = ByteBuffer.allocate(1024);
/**
* 将channel再次注册到selector上,监听他的可读事件
*/
String request = "";
while(socketChannel.read(buffer) > 1) {
/**
* 切换为读模式
*/
buffer.flip();
/**
* 读取buffer中的内容
*/
request += Charset.forName("UTF-8").decode(buffer);
}
/**
* 将服务器端的信息打印出来
*/
socketChannel.register(selector, SelectionKey.OP_READ);
if(request.length() > 0) {
System.out.println(request);
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
//是一个阻塞方法,只有监听事件就绪了才会返回,
/**
* TODO 获取可用的channel数量
*/
int readyChannels = 0;
try {
readyChannels = selector.select();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if(readyChannels == 0) {
continue;
}
/**
* 获取可用Channel的集合
*/
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterable = selectionKeys.iterator();
while(iterable.hasNext()) {
/**
* selectionKey实例
*/
SelectionKey selectionKey = iterable.next();
/**
* 移除当前的selectionKey
*/
iterable.remove();
/**
* 如果是可读事件
*/
if(selectionKey.isReadable()) {
try {
readHandler(selectionKey, selector);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
有问题可以在下面评论,技术问题可以私聊我哦。