NIO
原理
graph TB
subgraph 客户端
id1[SocketChannel]==连接服务器==>id11[通道];
id3{客户端请求}--流-->id2{ByteBuffer};
id2-->id1;
id1-.读取.->id12[ByteBuffer];
id12-.信息.->id13[msgRes];
end
subgraph 服务器ServerSocketChannel
id11==>id21[通道];
id22[Selector]==轮询==>id21;
id22==注册==>id23[ServerSocketChannel]
id4[SocketChannel]==accept==>id21;
id4==投入线程池==>id24[newFixedThreadPool]
id24--读取-->id5[ByteBuffer];
id5--信息-->id7[msg];
id8[服务器响应]-.流.->id9[ByteBuffer];
id9.->id24;
end
实现
服务器端实现
import cn.mldn.info.HostInfo;
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.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NIOEchoServer {
private static class EchoClientHandler implements Runnable {
// 客户端通道
private SocketChannel clientChannel ;
// 循环处理标记
private boolean flag = true ;
public EchoClientHandler(SocketChannel clientChannel) {
this.clientChannel = clientChannel ;
// 严格意义上来讲,当已经成功的连接上了服务器,并且需要进行进一步处理之前要发送一些消息给客户端
}
@Override
public void run() {
// 50个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(50) ;
try {
// 需要不断进行交互
while(this.flag) {
// 清空缓冲区
buffer.clear() ;
// 向缓冲区之中读取数据
int readCount = this.clientChannel.read(buffer) ;
String readMessage = new String(buffer.array(),0,readCount).trim() ;
// 回应数据信息
String writeMessage = "【ECHO】" + readMessage + "\n" ;
System.out.println("收到客户端消息:"+readMessage);
if("byebye".equalsIgnoreCase(readMessage)) {
writeMessage = "【EXIT】拜拜,下次再见!" ;
this.flag = false ;
}
// 数据输入通过缓存的形式完成,而数据的输出同样需要进行缓存操作
// 为了写入新的返回数据而定义
buffer.clear() ;
// 发送内容
buffer.put(writeMessage.getBytes()) ;
// 重置缓冲区
buffer.flip() ;
// 回应数据
this.clientChannel.write(buffer) ;
}
this.clientChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
// 1、NIO的实现考虑到性能的问题以及响应时间问题,需要设置一个线程池,采用固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 2、NIO的处理是基于Channel控制的,所以有一个Selector就是负责管理所有的Channel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 3、需要为其设置一个非阻塞的状态机制
serverSocketChannel.configureBlocking(false); // 非阻塞模式
// 4、服务器上需要提供有一个网络的监听端口
serverSocketChannel.bind(new InetSocketAddress(HostInfo.PORT));
// 5、需要设置一个Selector,作为一个选择器的出现,目的是管理所有的Channel
Selector selector = Selector.open();
// 6、将当前的Channel注册到Selector之中
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 连接时处理
System.out.println("服务器已经启动成功,服务器的监听端口为:" + HostInfo.PORT);
// 7、NIO采用的是轮询模式,每当发现有用户连接的时候就需要启动一个线程(线程池管理)
int keySelect = 0; // 接收轮询状态
while((keySelect = selector.select()) > 0) { // 实现了轮询处理
Set<SelectionKey> selectionKeys = selector.selectedKeys() ; // 获取全部的Key
Iterator<SelectionKey> selectionIter = selectionKeys.iterator() ;
while(selectionIter.hasNext()) {
SelectionKey selectionKey = selectionIter.next() ; // 获取每一个Key的信息
if (selectionKey.isAcceptable()) { // 为连接模式
SocketChannel clientChannel = serverSocketChannel.accept() ; // 等待连接
if (clientChannel != null) {
executorService.submit(new EchoClientHandler(clientChannel)) ;
}
}
selectionIter.remove();
}
}
executorService.shutdown();
serverSocketChannel.close();
}
}
客户端实现
import cn.mldn.info.HostInfo;
import cn.mldn.util.InputUtil;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NIOEchoClient {
public static void main(String[] args) throws Exception {
SocketChannel clientChannel = SocketChannel.open() ; // 打开客户端连接通道
clientChannel.connect(new InetSocketAddress(HostInfo.HOST_NAME,HostInfo.PORT)) ;// 连接
ByteBuffer buffer = ByteBuffer.allocate(50) ;// 开辟缓冲区
boolean flag = true ;
while(flag) {
buffer.clear() ; // 清空缓冲区
String inputData = InputUtil.getString("请输入要发送的信息:").trim() ;
buffer.put(inputData.getBytes()) ; // 将输入的数据保存在缓冲区之中
buffer.flip() ; // 重置缓冲区
clientChannel.write(buffer) ; // 发送数据
buffer.clear() ; // 在读取之前进行缓冲区清空
int readCount = clientChannel.read(buffer) ;
buffer.flip() ;
System.err.println(new String(buffer.array(),0,readCount));
if("byebye".equalsIgnoreCase(inputData)) {
flag = false ;
}
}
clientChannel.close();
}
}
上一篇 网络编程(二)BIO
下一篇 网络编程(四)AIO