在面试中NIO的东西还是有很多会被面到,那么NIO是什么?
从古老的套接字(socket)说起,传统的socket编程为新启动一个服务,阻塞到这,一个Client端连接过来,new 一个Thread 去处理
总结顺便学习一下IDEA ,破解版http://xidea.online,
package com.wpx.bio;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务端 通过一个端口对外提供服务
* 当一个Client端连接后通过新启动一个线程去处理
*/
class Server {
final static int PORT = 8888;
public static void main(String[] args) {
ServerSocket ss = null;
try {
ss = new ServerSocket(PORT);
System.out.println("Server start....");
Socket socket = ss.accept();
//new Thread Handler
new Thread(new ServerHandler(socket)).start();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(ss != null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
ss = null;
}
}
}
package com.wpx.bio;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**\
* 服务端处理类需要jichengThread或者实现Runnable
*/
public class ServerHandler implements Runnable{
private Socket socket ;
/**
* 构造 Alt+Insert
* @param socket
*/
public ServerHandler(Socket socket){
this.socket = socket;
}
/**
* Ctrl+O
*/
@Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
out = new PrintWriter(this.socket.getOutputStream(), true);
String body = null;
while(true){
body = in.readLine();
if(body == null) break;
System.out.println("Server :" + body);
out.println("服务器端回送响的应数据.");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
socket = null;
}
}
}
客户端
package com.wpx.bio;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务端 通过一个端口对外提供服务
* 当一个Client端连接后通过新启动一个线程去处理
*/
class Server {
final static int PORT = 8888;
public static void main(String[] args) {
ServerSocket ss = null;
try {
ss = new ServerSocket(PORT);
System.out.println("Server start....");
Socket socket = ss.accept();
//new Thread Handler
new Thread(new ServerHandler(socket)).start();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(ss != null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
ss = null;
}
}
}
因为每一个客户端的连接都会新启动一个线程,给服务器造成了巨大压力,在没有NIO之前,通过使用线程池+一个阻塞的队列来缓解服务器压力
package com.wpx.bio2;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.Buffer;
/**
* 与开始处理思路大体一致
* 不同的是新的客户端连接后偶交由线程池处理
*/
public class Server {
final static int PORT=8888;
public static void main(String[] args) {
ServerSocket ss=null;
BufferedReader in=null;
PrintWriter out=null;
try{
ss=new ServerSocket(PORT);
System.out.println("Server satrt.....");
Socket socket = null;
HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 1000);
while(true){
socket = ss.accept();
executorPool.execute(new ServerHandler(socket));
}
}catch (Exception e){
e.printStackTrace();
new RuntimeException("服务端异常");
}finally {
if(in != null){
try {
in.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(ss != null){
try {
ss.close();
} catch (Exception e3) {
e3.printStackTrace();
}
}
ss = null;
}
}
}
线程池
package com.wpx.bio2;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class HandlerExecutorPool {
private ExecutorService executor;
public HandlerExecutorPool(int maxPoolSize, int queueSize){
this.executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
maxPoolSize,
120L,
TimeUnit.SECONDS,
new ArrayBlockingQueue(queueSize));
}
public void execute(Runnable task){
this.executor.execute(task);
}
}
处理类
package com.wpx.bio2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class ServerHandler implements Runnable{
private Socket socket;
public ServerHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
out = new PrintWriter(this.socket.getOutputStream(), true);
String body = null;
while(true){
body = in.readLine();
if(body == null) break;
System.out.println("Server:" + body);
out.println("Server response");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(in != null){
try {
in.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (Exception e3) {
e3.printStackTrace();
}
}
socket = null;
}
}
}
客户端
package com.wpx.bio2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
final static String ADDRESS = "127.0.0.1";
final static int PORT =8888;
public static void main(String[] args) {
Socket socket=null;
BufferedReader in=null;
PrintWriter out=null;
try{
socket=new Socket(ADDRESS,PORT );
in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
out=new PrintWriter(socket.getOutputStream());
out.print("Client Request");
String response= in.readLine();
System.out.println("Clent"+response);
}catch (Exception e){
e.printStackTrace();
}finally {
if(in != null){
try {
in.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (Exception e3) {
e3.printStackTrace();
}
}
socket = null;
}
}
}
2.NIO1.0位同步非阻塞
package com.wpx.nio;
import java.nio.IntBuffer;
public class BufferTest {
public static void main(String[] args) {
//创建指定长度的缓存区 Buffre不仅仅只有Int类型的,仅依次为了例
//创建指定长度
/**
* 生成返回值类型 Alt+Enter
*/
IntBuffer buf = IntBuffer.allocate(10);
buf.put(1);// position位置:0 - > 1
buf.put(2);// position位置:1 - > 2
buf.put(3);// position位置:2 - > 3
//把位置复位为0,也就是position位置:3 - > 0
buf.flip();
System.out.println("容量为 "+buf.capacity()); //容量初始化后不允许发生改变(warp方法包裹数组除外)
System.out.println("限制为"+buf.limit()); //实际装载元素
System.out.println("获取下标为1的元素:" + buf.get(1));
System.out.println("get(index)方法,position位置不改变:" + buf);
buf.put(1, 4);
System.out.println("put(index, change)方法,position位置不变:" + buf);;
for (int i = 0; i < buf.limit(); i++) {
//调用get方法会使其缓冲区位置(position)向后递增一位
System.out.print(buf.get() + "\t");
}
System.out.println("buf对象遍历之后为: " + buf);
// 2 wrap方法使用
/**
// wrap方法会包裹一个数组: 一般这种用法不会先初始化缓存对象的长度,因为没有意义,最后还会被wrap所包裹的数组覆盖掉。
// 并且wrap方法修改缓冲区对象的时候,数组本身也会跟着发生变化。
int[] arr = new int[]{1,2,5};
IntBuffer buf1 = IntBuffer.wrap(arr);
System.out.println(buf1);
IntBuffer buf2 = IntBuffer.wrap(arr, 0 , 2);
//这样使用表示容量为数组arr的长度,但是可操作的元素只有实际进入缓存区的元素长度
System.out.println(buf2);
*/
// 3 其他方法
/**
IntBuffer buf1 = IntBuffer.allocate(10);
int[] arr = new int[]{1,2,5};
buf1.put(arr);
System.out.println(buf1);
//一种复制方法
IntBuffer buf3 = buf1.duplicate();
System.out.println(buf3);
//设置buf1的位置属性
//buf1.position(0);
buf1.flip();
System.out.println(buf1);
System.out.println("可读数据为:" + buf1.remaining());
int[] arr2 = new int[buf1.remaining()];
//将缓冲区数据放入arr2数组中去
buf1.get(arr2);
for(int i : arr2){
System.out.print(Integer.toString(i) + ",");
}
*/
}
}
服务端
package com.wpx.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
/**
*必须实现Runnable接口
* 新启动一个线程让Selector处于一种轮询的状态
*
* @author wangpx
*/
public class Server implements Runnable{
//多路复用器
private Selector selector;
//建立缓存区 2的倍数更高效
private ByteBuffer readBuf=ByteBuffer.allocate(1024);
private ByteBuffer writeBuf=ByteBuffer.allocate(1024);
public Server(int port) {
try{
//打开多路复用器
this.selector=Selector.open();
//打开服务通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//将其设置为非阻塞莫斯
ssc.configureBlocking(false);
//绑定地址
ssc.bind( new InetSocketAddress(port));
//将服务器通道注册到多路复用器上,并且监听阻塞事件
ssc.register(this.selector, SelectionKey.OP_ACCEPT);
System.out.println("Server start and port is :" +port);
}catch(Exception e){
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
try {
// 让多路复用器开始监听
this.selector.select();
//返回多路复用器已经被选择的结果集
Iterator keys = this.selector.selectedKeys().iterator();
//进行遍历
while(keys.hasNext()) {
//获取一个选择的元素
SelectionKey key = keys.next();
//直接从容器中移除就可以了
keys.remove();
//如果是有效的
if(key.isValid()){
//如果为阻塞状态
if(key.isAcceptable()){
this.accept(key);
}
//如果是可读状态
if (key.isReadable()){
this.read(key);
}
//如果是可写状态
if(key.isWritable()){
this.write(key);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void write(SelectionKey key) {
}
private void read(SelectionKey key) {
try{
//清空缓冲区的数据
this.readBuf.clear();
//获取之前注册的socket通道对象
SocketChannel sc= (SocketChannel) key.channel();
int read = sc.read(this.readBuf);
if (read == -1){
key.channel().close();
key.cancel();
return ;
}
//有数据则进行读取,读取前要复位方法,详情见上面的demo
this.readBuf.flip();
//根据缓冲区的数据长度创建相对的byte数组,接受缓冲区的数据
byte[] bytes=new byte[this.readBuf.remaining()];
//接受缓冲区数据
this.readBuf.get(bytes);
//打印结果
String body=new String(bytes).trim();
System.out.println("Server: "+body);
}catch(Exception e){
e.printStackTrace();
}
}
private void accept(SelectionKey key) {
try{
//获取服务通道
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//执行阻塞方法
SocketChannel sc = ssc.accept();
//设置阻塞模式
sc.configureBlocking(false);
//注册到多路复用器上并设置读取标识别
sc.register(this.selector,SelectionKey.OP_READ);
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
new Thread(new Server(8888)).start();
}
}
客户端
package com.wpx.nio;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class Client {
public static void main(String[] args) {
InetSocketAddress address=new InetSocketAddress("127.0.0.1",8888);
//声明连接通道
SocketChannel sc=null;
//建立缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
try{
//打开通道
sc = SocketChannel.open();
sc.connect(address);
while(true) {
//定义一个字节数组,然后使用系统录入功能
byte[] bytes=new byte[1024];
System.in.read(bytes);
//把数据放到缓冲
buf.put(bytes);
//对缓冲区进行复位
buf.flip();
//写出数据
sc.write(buf);
//清空缓冲数据
buf.clear();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if ( sc!=null){
try{
sc.close();
}catch(Exception e){
sc=null;
}
}
}
}
}
将复杂的Selector屏蔽,并且引入了线程组
服务端
package com.wpx.aio;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server {
//线程池
private ExecutorService executorService;
//线程组
private AsynchronousChannelGroup asynchronousChannelGroup;
//服务器通道
public AsynchronousServerSocketChannel asynchronousServerSocketChannel;
public Server(int port) {
try{
//创建一个缓存池
executorService= Executors.newCachedThreadPool();
//创建一个线程组
asynchronousChannelGroup=AsynchronousChannelGroup.withCachedThreadPool(executorService,1);
//创建服务器通道
asynchronousServerSocketChannel=AsynchronousServerSocketChannel.open(asynchronousChannelGroup);
//进行绑定
asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
System.out.println("server start , port : " + port);
//进行阻塞
asynchronousServerSocketChannel.accept(this, new ServerCompletionHandler());
//一直阻塞 不让服务器停止
Thread.sleep(Integer.MAX_VALUE);
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
Server s=new Server(8888);
}
}
package com.wpx.aio;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;
public class ServerCompletionHandler implements CompletionHandler{
@Override
public void completed(AsynchronousSocketChannel asc, Server attachment) {
//当有下一个客户端接入的时候 直接调用Server的accept方法,这样反复执行下去,保证多个客户端可以阻塞
attachment.asynchronousServerSocketChannel.accept(attachment,this);
read(asc);
}
private void read(final AsynchronousSocketChannel asc) {
//读取数据
ByteBuffer buf = ByteBuffer.allocate(1024);
asc.read(buf, buf, new CompletionHandler() {
@Override
public void completed(Integer resultSize, ByteBuffer attachment) {
//进行读取之后,重置标识位
attachment.flip();
//获得读取的字节数
System.out.println("Server -> " + "收到客户端的数据长度为:" + resultSize);
//获取读取的数据
String resultData = new String(attachment.array()).trim();
System.out.println("Server -> " + "收到客户端的数据信息为:" + resultData);
String response = "服务器响应, 收到了客户端发来的数据: " + resultData;
write(asc, response);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
exc.printStackTrace();
}
});
}
private void write(AsynchronousSocketChannel asc, String response) {
try {
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.put(response.getBytes());
buf.flip();
asc.write(buf).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Server attachment) {
exc.printStackTrace();
}
}
package com.wpx.aio;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
public class Client implements Runnable{
private AsynchronousSocketChannel asc ;
public Client() throws Exception {
asc = AsynchronousSocketChannel.open();
}
public void connect(){
asc.connect(new InetSocketAddress("127.0.0.1", 8888));
}
public void write(String request){
try {
asc.write(ByteBuffer.wrap(request.getBytes())).get();
read();
} catch (Exception e) {
e.printStackTrace();
}
}
private void read() {
ByteBuffer buf = ByteBuffer.allocate(1024);
try {
asc.read(buf).get();
buf.flip();
byte[] respByte = new byte[buf.remaining()];
buf.get(respByte);
System.out.println(new String(respByte,"utf-8").trim());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
}
}
public static void main(String[] args) throws Exception {
Client c1 = new Client();
c1.connect();
Client c2 = new Client();
c2.connect();
Client c3 = new Client();
c3.connect();
new Thread(c1, "c1").start();
new Thread(c2, "c2").start();
new Thread(c3, "c3").start();
Thread.sleep(1000);
c1.write("c1 aaa");
c2.write("c2 bbbb");
c3.write("c3 ccccc");
}
}