本章节,重点聊一下NIO中的多路复用器Selector、SocketChannel、ByteBuffer、SelectorKey
场景:
客户端向Selector发起连接事件,调用selector.select(),这个方法会阻塞执行,也可以设置超时时间;
服务端收到连接事件,会向ByteBuffer写入信息,之后会向Selector注册读事件;
客户端的selector监听到读事件,这时会读取服务端写入的信息;
注:
多路复用需要采用非阻塞的IO,所以代码需要设置:socketChannel.configureBlocking(false);
原理深入分析:
需要分析poll/epoll(Event poll)底层原理,这里不做深入分析.
package com.jason.thread.socket.nio.step2;
import com.alibaba.fastjson.JSONObject;
import com.jason.thread.socket.body.ServiceRequest;
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
/**
* @program: JasonSpringMybatis
* @description
* @author: 大龄程序猿
* @create: 2020-06-10 21:27
**/
public class NewIoServer {
private static Selector selector;
public ServiceRequest getRequest() {
return request;
}
public void setRequest(ServiceRequest request) {
this.request = request;
}
private ServiceRequest request;
public static void main(String[] args) {
try {
//多路复用一定要是非阻塞的
selector=Selector.open();
System.out.println(selector.hashCode());
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);//设置连接非阻塞
serverSocketChannel.bind(new InetSocketAddress(8080));
//注册连接事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while(true)
{
System.out.println("服务端等待获取事件驱动:");
selector.select();//阻塞调用,由事件驱动
System.out.println("捕获到事件驱动:");
Set selectionKeySet= selector.selectedKeys();
Iterator iterator=selectionKeySet.iterator();
while(iterator.hasNext())
{
SelectionKey selectionKey=iterator.next();
try {
iterator.remove();
if(selectionKey.isAcceptable()){//连接事件
System.out.println("服务端连接开始:");
handlerAccept(selectionKey);
}else if(selectionKey.isReadable())//读事件
{
System.out.println("服务端读开始:");
handlerRead(selectionKey);
}
}catch (IOException e)
{
selectionKey.cancel();
continue;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void handlerRead(SelectionKey selectionKey) throws Exception {
SocketChannel socketChannel=(SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
try {
socketChannel.configureBlocking(false);
socketChannel.read(byteBuffer);
ServiceRequest serviceRequest=JSONObject.parseObject(new String(byteBuffer.array()),ServiceRequest.class);
ServiceRequest.RequestData requestData=serviceRequest.getData();
System.out.println("收到来自:["+serviceRequest.getFromTalker()+"] 的消息;消息标题:["+requestData.getTitle()+"]"
+";消息内容:["+requestData.getContent()+"]");
requestData.setTitle("互联网架构师内容");
requestData.setContent("努力学习,成为一流的架构师");
serviceRequest.setFromTalker("Tom");
serviceRequest.setToTalker("Jerry");
serviceRequest.setData(requestData);
String response=JSONObject.toJSONString(serviceRequest);
System.out.println("response="+response);
byteBuffer.clear();
socketChannel.write(byteBuffer.wrap(response.getBytes()));
System.out.println("发送读事件:");
socketChannel.register(selector,SelectionKey.OP_READ );//注册读驱动
} catch (IOException e) {
e.printStackTrace();
throw new IOException("新建异常");
}
}
private static void handlerAccept(SelectionKey selectionKey) {
ServerSocketChannel serverSocketChannel=(ServerSocketChannel)selectionKey.channel();
try {
SocketChannel socketChannel =serverSocketChannel.accept();
socketChannel.configureBlocking(false);//设置非阻塞
System.out.println("连接就绪,注册读事件");
socketChannel.write(ByteBuffer.wrap("Hello Client,I'am NIO Server!".getBytes()));
System.out.println("发送读事件:");
socketChannel.register(selector,SelectionKey.OP_READ );//注册读驱动
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.jason.thread.socket.nio.step2;
import com.alibaba.fastjson.JSONObject;
import com.jason.thread.socket.body.ServiceRequest;
import org.junit.Test;
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.util.Iterator;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @program: JasonSpringMybatis
* @description
* @author: 大龄程序猿
* @create: 2020-06-13 19:36
**/
public class KeepAlive implements Runnable{
private static Selector selector;
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(new KeepAlive(),1,8, TimeUnit.SECONDS);
}
@Override
public void run() {
try
{
selector=Selector.open();
System.out.println(selector.hashCode());
SocketChannel socketChannel=SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost",8080));
socketChannel.register(selector, SelectionKey.OP_CONNECT);
while(true)
{
System.out.println("客户端阻塞等待事件驱动:");
selector.select(2000);//阻塞
System.out.println("捕获到事件:");
Set selectionKeySet = selector.selectedKeys();
Iterator iterator = selectionKeySet.iterator();
if(!iterator.hasNext())
{
System.out.println("等待超时,未获取到服务端的响应,当前线程退出!!");
break;
}
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
System.out.println("SelectionKey=="+key.hashCode());
iterator.remove();
if (key.isConnectable())//连接事件
{
System.out.println("客户端连接开始:");
handleSocketChannel(key);
}else if(key.isReadable())
{
System.out.println("客户端读开始:");
HandleRead(key);
}
}
}
}catch(Exception e)
{
e.printStackTrace();
}
}
private static void HandleRead(SelectionKey key) throws IOException {
SocketChannel socketChannel=(SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
socketChannel.read(buffer);
System.out.println(new String(buffer.array()));
}
private static void handleSocketChannel(SelectionKey key) throws IOException {
SocketChannel channel=(SocketChannel)key.channel();
if(channel.isConnectionPending())
{
channel.finishConnect();
}
channel.configureBlocking(false);
channel.write(ByteBuffer.wrap(initRequest().getBytes()));
channel.register(selector,SelectionKey.OP_READ);
}
@Test
public static String initRequest()
{
ServiceRequest request=new ServiceRequest(true,"jerry","Jaosn");
ServiceRequest.RequestData requestData= request.new RequestData("AA","CC");
request.setData(requestData);
String jason= JSONObject.toJSONString(request);
return jason;
}
}