架构图:
服务端代码改造
1 Reactor
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NIOReactorServer {
//服务所用端口
private static final int PORT=9999;
//选择器
private Selector selector;
//服务端serverSocketChannel, 主要功能是处理事件
private ServerSocketChannel serverSocketChannel;
//处理连接的acceptor
private Acceptor acceptor;
//处理业务部分的handler
private MsgHandler msgHandler;
//初始化服务端
{
try {
//创建选择器
selector=Selector.open();
//创建serverSocketChannel
serverSocketChannel=ServerSocketChannel.open();
//设置为非阻塞式
serverSocketChannel.configureBlocking(false);
//绑定端口
serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
//把serverSocketChannel注册到选择器,使选择器监听accept事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}catch (Exception e){
e.printStackTrace();
}
}
public Acceptor getAcceptor() {
return acceptor;
}
public void setAcceptor(Acceptor acceptor) {
this.acceptor = acceptor;
}
public MsgHandler getMsgHandler() {
return msgHandler;
}
public void setMsgHandler(MsgHandler msgHandler) {
this.msgHandler = msgHandler;
}
public void dispatch(){
//当有事件发生时,获取发生事件的SelectionKey集合,通过SelectionKey可以获取socketChannel通道和发生事件的类型等
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
System.out.print("监听到事件:");
SelectionKey selectionKey = iterator.next();
//accept事件处理
if (selectionKey.isAcceptable()) {
System.out.println("连接事件");
if(acceptor!=null)
acceptor.accept(selector,serverSocketChannel);
}
//read事件处理
if (selectionKey.isReadable()) {
System.out.println("消息读取事件");
//群发消息(不包含自己)
if(msgHandler!=null) {
//读消息
String msg=msgHandler.read(selectionKey);
System.out.println("服务器群发消息:" + msg);
//处理消息
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
String userName ="未知";
try {
userName = socketChannel.getRemoteAddress().toString().substring(11);
}catch (Exception e){
e.printStackTrace();
}
String sendMsg=msgHandler.getSendMsg(userName,msg);
//发送消息
msgHandler.send(selector, selectionKey, sendMsg);
}
}
//移出处理完事件的selectionKey
iterator.remove();
}
}
//事件监听:accept和read事件
public void select(){
while (true) {
try {
System.out.println("开始监听事件");
//当没有事件发生时会阻塞在此位置,也可设置不阻塞或阻塞超时时间
selector.select();
//事件分发
dispatch();
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//创建服务端
NIOReactorServer server=new NIOReactorServer();
//指定连接处理器
server.setAcceptor(new Acceptor());
//指定发送消息处理器
server.setMsgHandler(new MsgHandler());
//开始监听
server.select();
}
}
Acceptor
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/**
* 处理连接对象
*/
public class Acceptor {
public void accept(Selector selector, ServerSocketChannel serverSocketChannel) {
try {
//使用serverSocketChannel分配一个代表客户端连接的socketChannel
SocketChannel socketChannel = serverSocketChannel.accept();
//设置为非阻塞式
socketChannel.configureBlocking(false);
//注册到选择器上,并让选择器监听read事件
socketChannel.register(selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
}
MsgHandler
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
/**
* 处理业务部分的handler
*/
public class MsgHandler {
//读取从客户端发来的数据
public String read(SelectionKey self) {
try {
//获取事件发生、代表客户端连接的socketChannel
SocketChannel socketChannel = (SocketChannel) self.channel();
//读取通道的数据到缓存,用来记录日志
ByteBuffer b = ByteBuffer.allocate(1024);
int read = socketChannel.read(b);
return new String(b.array(), 0, read);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//群发消息(不包含自己)
public void send(final Selector selector,final SelectionKey self, final String msg) {
System.out.println("一共有" + selector.keys().size() + "个连接");
selector.keys().stream().filter(selectionKey -> self != selectionKey).forEach(selectionKey -> {
try {
//群发消息(ServerSocketChannel除外)
if (selectionKey.channel() instanceof SocketChannel) {
//获取代表客户端连接的socketChannel通道
SocketChannel socketChannel1 = (SocketChannel) selectionKey.channel();
//把缓存内的数据写入到socketChannel通道
socketChannel1.write(ByteBuffer.wrap(msg.getBytes()));
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
//获取业务数据
public String getSendMsg(String userName, String msg) {
return userName + "说:"+msg;
}
}
客户端(未改动)
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
public class NIOClient {
// 客户端socketChannel
private SocketChannel socketChannel;
{
try {
//创建socketChannel
socketChannel=SocketChannel.open();
//设置非阻塞式
socketChannel.configureBlocking(false);
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9999);
// 创建连接
if(!socketChannel.connect(inetSocketAddress)){
while (!socketChannel.finishConnect()){
System.out.println("客户端不会阻塞,可以做其他工作");
}
}
//连接成功后开启一个线程定时2秒接收服务器转发的消息
receiveMsg();
} catch (IOException e) {
e.printStackTrace();
}
}
// 收消息线程
private void receiveMsg(){
new Thread(()->{
System.out.println("开始收取信息...");
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) {
try {
//把通道的数据写入缓存buffer
int read = socketChannel.read(buffer);
if(read>0)
System.out.println(new String(buffer.array(),0,read));
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}finally {
//清空缓存buffer
buffer.clear();
}
}
}).start();
}
//发送消息
public void sendMsg(String msg){
try {
//发送消息
socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
NIOClient client=new NIOClient();
// 循环发送消息
Scanner scanner=new Scanner(System.in);
while (scanner.hasNextLine()) {
try {
client.sendMsg(scanner.nextLine());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}