NIO 单线程完成的在线字符数统计用例
2022-06-11 本文已影响0人
JohnYuCN
package cn.johnyu;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.Iterator;
public class MyApp {
public static void main(String[] args) throws Exception{
ByteBuffer buffer=ByteBuffer.allocate(1024);
Selector selector = Selector.open();
int userNum=0;
/* 打开ServerSocketChannel,并以名称为 ACCEPT 的事件向Selector注册 */
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
/* 监听6000端口,但主线程并不阻塞在此处 */
serverSocketChannel.bind(new InetSocketAddress(6000));
System.out.println("服务器启动,监听 6000 端口...");
while (true){
/* 此处是程序的关键,如没有任何注册事件发生,主线程将在这阻塞 */
selector.select();
/*一旦有注册的事件的发生,主线程将在此向下执行*/
/* 执行的逻辑:
遍历事件队列,根据事件队列的名称及相关条件进行事件的回调处理;
如果是 ACCEPT ,则获取相应的 SocketChannel,并向Selector 进行注册(READ事件);
再向用户输出提示,如果用户输入是 bye 则表示输入结束 */
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isAcceptable()){
++userNum;
ServerSocketChannel channel = (ServerSocketChannel)selectionKey.channel();
SocketChannel socketChannel = channel.accept();
socketChannel.configureBlocking(false);
//ScokcetChannel 以 事件 READ 注册,同是增加一个附加对象,用于绑定会话级的信息,本例表示总计数器
socketChannel.register(selector,SelectionKey.OP_READ,new Counter(0));
buffer.put(("您是系统的第 "+userNum+" 个用户,请输入信息,我将为您统计字数(输入 bye 退出!):\n").getBytes("utf8"));
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
System.out.println("有客户连接成功,当前用户数:"+userNum);
}
if(selectionKey.isReadable()){
SocketChannel channel= (SocketChannel)selectionKey.channel();
//获取用户输入的信息和输入的字符数,如果字符数等于0时,表示异常退出,大于0表示正常输入
int len=channel.read(buffer);
if(len>0){
//从Buffer中copy数组并转换成为数组
String userMessage=new String(Arrays.copyOfRange(buffer.array(),0,len),"utf8");
//用户输入 bye 时的处理:关闭Channel和连接,并减少用户数量
if(userMessage.trim().equals("bye")){
channel.close();
selectionKey.cancel();
userNum--;
System.out.println("有用户正常退出,当前用户数是:"+userNum +"个");
}
else{
Counter totalCounter=(Counter)selectionKey.attachment();//获取注册时绑定的会话级对象
totalCounter.incr(userMessage.length()-1);//累计字符数
String resp="您输入的字符个数:"+(userMessage.length()-1)+",您输入的总字符个数是:"+totalCounter.getCount()+",总用户数:"+userNum+"\n";
/*以下四行代码逻辑:
向空的Buffer中写入信息,复位position=0,limit=字节数,向Channel中写入,清空Buffer*/
buffer.put(resp.getBytes("utf8"));
buffer.flip();
channel.write(buffer);
buffer.clear();
}
}
else{//用户强制断网时
channel.close();
selectionKey.cancel();
userNum--;
System.out.println("异常退出,当前用户数是:"+userNum +"个");
}
}
iterator.remove();
}
}
}
}
class Counter{
public Counter(int count) {
this.count = count;
}
private int count;
public int getCount() {
return count;
}
public void incr(int step) {
this.count+=step;
}
}