这里将实现一个客户端和一个服务端,客户端发起一次请求,服务端接收请求并返回处理结果。
下文将根据这段代码说明BIO存在的问题。
/**
* @author GrainRain
* @date 2020/05/19 20:10
*
* Description BIOServer
**/
public class BIOServer {
public static final int PORT = 8090;
public static void main(String[] args){
ServerSocket server = null;
try {
server = new ServerSocket(PORT);
System.out.println("the server is started");
while (true) {
//每获取一个连接,将socket交给handler处理,此处发生阻塞
Socket socket = server.accept();
//此处可以采用线程池处理
new Thread(new BIOMessageHandler(socket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(server != null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
server = null;
}
}
}
}
}
/**
* @author GrainRain
* @date 2020/05/19 20:26
**/
public class BIOMessageHandler implements Runnable {
private Socket socket;
public BIOMessageHandler(Socket socket){
this.socket = socket;
}
@Override
public void run() {
InputStream in = null;
OutputStream out = null;
try {
in = socket.getInputStream();
byte[] bytes = new byte[2 << 20];
StringBuilder builder = new StringBuilder();
int len = -1;
while ((len = in.read(bytes)) != -1){
builder.append(new String(bytes,0,len));
}
System.out.println("receive message,the data is >>" + builder.toString());
out = socket.getOutputStream();
out.write("receive success, now time is"+ System.currentTimeMillis()).getBytes());
}catch (Exception e){
e.printStackTrace();
}finally {
close(in,out);
}
}
public void close(AutoCloseable ... autoCloseables){
if(autoCloseables==null){
return;
}
for (int i = 0; i < autoCloseables.length; i++) {
AutoCloseable autoCloseable = autoCloseables[i];
try {
autoCloseable.close();
} catch (Exception e) {
e.printStackTrace();
}finally {
autoCloseable = null;
}
}
}
}
/**
* @author GrainRain
* @date 2020/05/19 20:32
* Description BIO客户端
**/
public class BIOClient {
public static final String HOST = "127.0.0.1";
public static final int PORT = 8090;
public static void main(String[] args) {
Socket socket = null;
BufferedReader in = null;
OutputStream out = null;
try{
socket = new Socket(HOST,PORT);
out = socket.getOutputStream();
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.write("I'm client".getBytes());
//通知已完成写出
socket.shutdownOutput();
String back = in.readLine();
//通知已完成读入数据
socket.shutdownInput();
System.out.println("the client receive message >> " + back);
}catch(Exception e){
e.printStackTrace();
}finally {
}
}
public void close(AutoCloseable ... autoCloseables){
if(autoCloseables==null){
return;
}
for (int i = 0; i < autoCloseables.length; i++) {
AutoCloseable autoCloseable = autoCloseables[i];
try {
autoCloseable.close();
} catch (Exception e) {
e.printStackTrace();
}finally {
autoCloseable = null;
}
}
}
}
Java从1.7开始,对NIO的API进行了重大升级,实现了真正意义上的NIO支持。
Java NIO引入了3大的组件,包括buffer(缓冲区)、Channel(通道)、Selector(多路复用选择器)。通过这几大组件的分工协作,能够实现多路复用的非阻塞通信模型。
几大组件的详细介绍在此不做过多说明,读者可自行百度或翻阅源码。
以下是基于这几大组件实现的NIO模式的客户端与服务端代码。功能实现上上,仍然同前文的同步阻塞模型(BIO)一样。
/**
* @author GrainRain
* @date 2020/05/19 21:38
**/
public class NIOServer {
public static final int PORT = 8090;
public static void main(String[] args) {
new Thread(new MessageSelector(PORT)).start();
}
}
上面这段非常简单,就是启动一个多路复用选择器,通过多路复用选择器轮询监听Channel,将活跃的事件进行处理。
2. 以下是多路复用选择器线程,这部分非常关键。
/**
1. @author GrainRain
2. @date 2020/05/19 21:41
**/
public class MessageSelector implements Runnable{
private int port;
/**
* 多路复用选择器
* 通过selector实现一个线程处理多个请求
*/
private Selector selector;
private boolean isKeepAlive;
private ServerSocketChannel serverSocketChannel;
/**
* 消息处理器
*/
private NIOMessageHandler nioMessageHandler;
public MessageSelector(int port){
this.port = port;
nioMessageHandler = NIOMessageHandler.getInstance();
}
@Override
public void run() {
registrySelector();
this.isKeepAlive(true);
while (isKeepAlive){
try {
//轮询
selector.select(1000);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
selectionKeys.stream().forEach(selectionKey -> {
try {
//将轮询到的活跃请求交给handler处理
this.selectKeyHandler(selectionKey);
} catch (Exception e) {
if (selectionKey != null) {
selectionKey.cancel();
if (selectionKey.channel() != null) {
try {
selectionKey.channel().close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
});
}catch (Throwable e){
e.printStackTrace();
}
}
}
public void registrySelector(){
try {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
//设置为非阻塞
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(port));
//注册服务端serverSocketChannel的accept事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}catch (Exception e){
e.printStackTrace();
System.exit(1);
}
}
public boolean isKeepAlive(boolean isKeep){
this.isKeepAlive = isKeep;
return isKeep;
}
public void selectKeyHandler(SelectionKey selectionKey) throws IOException {
//新连接请求,获取连接,注册读事件
if (selectionKey.isAcceptable()){
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
//设为非阻塞
socketChannel.configureBlocking(false);
//注册读事件
socketChannel.register(selector,SelectionKey.OP_READ);
}
//如果是读事件,则交给另一组线程池进行业务逻辑处理
if(selectionKey.isReadable()){
SocketChannel socketChannel = null;
try {
socketChannel = (SocketChannel) selectionKey.channel();
nioMessageHandler.socketHandle(socketChannel);
}catch (Exception e){
socketChannel.close();
selectionKey.cancel();
}finally {
socketChannel = null;
}
}
}
}
/**
* @author GrainRain
* @date 2020/05/19 22:12
**/
public class NIOMessageHandler {
private static NIOMessageHandler nioMessageHandler = new NIOMessageHandler();
private ExecutorService threadPool;
private NIOMessageHandler(){
//初始化线程池
threadPool = new ThreadPoolExecutor(2,
5,
30,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1024));
}
public static NIOMessageHandler getInstance(){
return nioMessageHandler;
}
public void socketHandle(SocketChannel socketChannel){
if(socketChannel==null){
return;
}
threadPool.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//数据读取
int read = socketChannel.read(byteBuffer);
if (read > 0) {
Buffer buffer = byteBuffer.flip();
byte[] bytes = new byte[buffer.remaining()];
byteBuffer.get(bytes);
String msg = new String(bytes);
System.out.println("the server receive message >>" + msg);
String sendMsg = "hello, I'm server!!!";
//写出数据
socketChannel.write(ByteBuffer.wrap(sendMsg.getBytes()));
}
return null;
}
});
}
}
/**
* @author GrainRain
* @date 2020/05/19 22:28
**/
public class NIOClient {
public static final int PORT = 8090;
public static final int LOCAL_PORT = 8081;
public static final String IP = "127.0.0.1";
public static void main(String[] args) {
SocketChannel socketChannel = null;
try {
socketChannel = SocketChannel.open();
socketChannel.bind(new InetSocketAddress(LOCAL_PORT));
//设为非阻塞
socketChannel.configureBlocking(false);
//连接服务端
socketChannel.connect(new InetSocketAddress(IP,PORT));
while (!socketChannel.isConnected()){
socketChannel.finishConnect();
}
//发送数据
socketChannel.write(ByteBuffer.wrap("hello server, I'm client".getBytes()));
Thread.sleep(100);
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取数据
socketChannel.read(buffer);
buffer.flip();
System.out.println(new String(buffer.array(),0,buffer.limit()));
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
socketChannel = null;
}
}
}
}