同步和异步:
针对IO操作,如果此操作是由java自己完成的,那么这个操作就是同步操作
针对IO操作,如果此操作是由操作系统完成的,并在处理完成后通知我们的应用程序,所以我们的应用程序可以继续做其他事情,那么这个操作是异步
阻塞和非阻塞:
针对某些方法,如果此方法在执行时会发生阻塞等待,那么此方法就是阻塞的
针对某些方法,如果此方法在执行时不会发生阻塞,而是不管操作是否执行成功,都会立即返回一个状态值,那么此方法就是非阻塞的
/**
* AIO 异步非阻塞
* 无论BIO、AIO、NIO中,一旦建立连接了,两端都是平等的
* @aurthor chenl on 2018/12/15
*/
public class AioServerHandler implements CompletionHandler<AsynchronousSocketChannel, AioServer> {
/**
* 业务处理逻辑,当请求到来后,监听成功,应该做什么事情
* 一定要实现的逻辑:为下一次客户端请求开启监听,调用accept()
* @param result 就是和客户端直接建立关联的通道,有通道中的相关数据。如操作系统准备好的读取数据缓存,或等待返回数据的缓存
* @param attachment
*/
@Override
public void completed(AsynchronousSocketChannel result, AioServer attachment) {
// 处理下一次客户端请求,类似递归逻辑
attachment.getServerChannel().accept(attachment, this);
doRead(result);
}
private void doRead(final AsynchronousSocketChannel channel) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
/**
* 异步读操作
* read(
* ByteBuffer destination, 目的地,是处理客户端传递数据的中转缓存,可不用
T attachment, 处理客户端传递数据的对象,通常用Buffer处理
CompletionHandler<Integer,? super T> handler)) 处理逻辑
*
*/
channel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
/**
* 业务逻辑,读取客户端传输数据
* @param result
* @param attachment 在completed方法执行时,操作系统已经将客户端请求的数据写入到Buffer缓存中了,但为复位flip(),使用前一定要复位
*/
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println(attachment.capacity());
// 复位
attachment.flip();
try {
System.out.println("from client : " + new String(attachment.array(), "UTF-8"));
doWriter(channel);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
});
}
/**
* 真实项目中,服务器返回的结果应该是根据客户端的请求计算得到的。
* @param channel
*/
private void doWriter(AsynchronousSocketChannel channel) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
System.out.println("enter message send to client > ");
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
try {
buffer.put(line.getBytes("UTF-8"));
// 必须复位
buffer.flip();
// writer()方法是异步操作,具体实现由操作系统实现。可增加get()方法,实现阻塞,等待操作系统的写操作结束
channel.write(buffer);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 异常处理逻辑,当服务端代码出现异常的时候,应该做什么事情
* @param exc
* @param attachment
*/
@Override
public void failed(Throwable exc, AioServer attachment) {
exc.printStackTrace();
}
}
/**
* AIO 异步非阻塞
* @aurthor chenl on 2018/12/15
*/
public class AioClient {
private AsynchronousSocketChannel channel;
public static void main(String[] args) {
AioClient client = new AioClient("localhost", 9999);
try {
System.out.println("enter message send to server > ");
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
client.write(line);
client.read();
} finally {
client.destory();
}
}
public AioClient(String host, int port) {
init(host, port);
}
private void init(String host, int port) {
InetSocketAddress ip = new InetSocketAddress(host, port);
try {
// 开启客户端通道
channel = AsynchronousSocketChannel.open();
// 发起请求,建立连接
channel.connect(ip);
} catch (IOException e) {
e.printStackTrace();
}
}
public void write(String line) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
try {
buffer.put(line.getBytes("UTF-8"));
buffer.flip();
channel.write(buffer);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public void read() {
ByteBuffer buffer = ByteBuffer.allocate(1024);
// read方法是异步方法,由操作系统实现
Future<Integer> future = channel.read(buffer);
try {
// get方法是阻塞方法,会等待操作系统处理结束再返回
future.get();
buffer.flip();
System.out.println("from server : " + new String(buffer.array(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public void destory() {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
channel = null;
}
}
/**
* AIO 异步非阻塞
* @aurthor chenl on 2018/12/15
*/
public class AioServer {
private static final Integer DEFAULT_PORT = 9999;
/**
* 线程池提高服务端效率
*/
private ExecutorService service;
/**
* 服务端通道,针对服务端定义的端口号
*/
private AsynchronousServerSocketChannel serverChannel;
public AioServer(int port) {
init(port);
}
private void init(int port) {
System.out.println("server starting at port : " + port + "....");
InetSocketAddress ip = new InetSocketAddress(port);
service = new ThreadPoolExecutor(4, 4, 0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
});
try {
// 开启服务端通道
serverChannel = AsynchronousServerSocketChannel.open();
// 绑定监听端口,服务器启动成功,但未监听请求
serverChannel.bind(ip);
System.out.println("server started...");
// 阻塞监听
// accept(T attachment, CompletionHandler<AsynchronousSocketChannel,? super T> handler);
serverChannel.accept(this, new AioServerHandler());
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
AioServer server = new AioServer(DEFAULT_PORT);
}
public ExecutorService getService() {
return service;
}
public void setService(ExecutorService service) {
this.service = service;
}
public AsynchronousServerSocketChannel getServerChannel() {
return serverChannel;
}
public void setServerChannel(AsynchronousServerSocketChannel serverChannel) {
this.serverChannel = serverChannel;
}
}
- 创建Server端,开启Server端通道,绑定端口。
- accept阻塞等待客户端请求.
- 创建客户端,开启客户端通道,建立连接,发起请求
- accept监听到请求,创建Handler处理器对象,将Server自身和处理器交于accept方法执行
- accept执行时,创建客户端通道对象交给Handler,切换环境到Handler对象内部
- 在Handler内部处理时,通过Server对象调用accept()开启监听,以监听下一次请求
- 异步读(服务端操作系统等待客户端发送数据过来。)客户端通道缓存中的数据
- 客户端以write异步写来发起请求数据“你好”(客户端操作系统将请求数据写入到客户端通道,通知客户端writer()完毕)
- 服务端操作系统读取客户端通道中数据到缓存,并通知服务端read()完毕。
- 客户端以read异步读来读取响应数据(客户端操作系统等待从客户端通道中读取数据到缓存中,通知客户端端read()完毕)
- 服务端读取、处理请求数据,生成响应数据,以异步写(服务器操作系统将数据写入到客户端通道,并通知服务端write()完毕)入客户端通道缓存中
- 客户端get()阻塞等待通知,读取数据