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();
}
}
}
}
}
}
有問題可以在下面評論,技術問題可以私聊我哦。