Java BIO NIO AIO

IO这一块需要学习BIO、NIO、AIO,上层可以了解TCP、UDP原理

NIO图解

AIO图解

NIO 服务端代码

提示

  1. 类NIOServer用于启动NIO服务器,传输参数只有PORT
  2. 类NIOServerHandle用于NIO服务器处理客户端的请求
NIOServer启动类
package com.ruider.customerNIO;

/**
 * Created by mahede on 2018/11/20.
 */
public class NIOServer {
    private static final int NIO_PORT = 1234;
    private static NIOServerHandle nioServerHandle;

    public static void start() { start(NIO_PORT); }

    private synchronized static void start(int port) {
        if(nioServerHandle != null) { nioServerHandle.stop(); }
        nioServerHandle = new NIOServerHandle(NIO_PORT);
        new Thread(nioServerHandle, "Server").start();
    }

}
NIO服务器处理请求类
package com.ruider.customerNIO;

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;

/**
 * Created by mahede on 2018/11/20.
 */
public class NIOServerHandle implements Runnable{

    private int port;
    private ServerSocketChannel serverSocketChannel;
    private Selector selector;
    private volatile boolean start;

    public NIOServerHandle(int port) {
        this.port = port;
        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            start = true;
            System.out.println("NIOServer启动服务,端口:" + port);

        }catch (Exception e) {
            System.out.println("封装NIO服务失败");
            System.out.println(e);
        }
    }
    @Override
    public void run(){
        while(start) {
            try{
                //没1秒唤醒一次,轮询一遍事件
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey selectionKey = null;
                while(it.hasNext()) {
                    selectionKey = it.next();
                    it.remove();
                    try{
                        handleInput(selectionKey);
                    }catch(Exception e){
                        System.out.println("服务获取信息失败");
                        System.out.println(e);
                    }
                }
            }
            catch (Exception e) {
                System.out.println(e);
            }
        }
        if(selector != null)
            try{
                selector.close();
            }catch (Exception e) {
                e.printStackTrace();
            }

    }

    public void stop() { start = false;}

    private void handleInput(SelectionKey key) throws Exception{
        if(key.isValid()) {
            if(key.isAcceptable()) {
                ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                SocketChannel socketChannel = channel.accept();
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);

            }
            if(key.isReadable()) {
                SocketChannel socketChannel = (SocketChannel)key.channel();
                //分配1M的buffer
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                int size = socketChannel.read(byteBuffer);
                if(size > 0) {
                    //将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
                    byteBuffer.flip();
                    byte[] bytes =  new byte[byteBuffer.remaining()];
                    //复制到bytes中
                    byteBuffer.get(bytes);
                    String contents = new String(bytes, "UTF-8");
                    System.out.println("NIO服务收到消息:"+contents);

                    String expression = new String(bytes,"UTF-8");
                    System.out.println("服务器收到消息:" + expression);
                    doWrite(socketChannel, "收到信息,你好,我是NIO");
                }
                else if(size < 0) {
                    key.cancel();
                    socketChannel.close();
                }
            }
        }
    }

    private void doWrite(SocketChannel channel, String msg) {
        try {
            byte[] bytes = msg.getBytes();
            ByteBuffer writebuffer = ByteBuffer.allocate(bytes.length);
            writebuffer.put(bytes);
            //postion,limit值的交换,用于输出buffer
            writebuffer.flip();
            channel.write(writebuffer);
        }catch (Exception e) {
            System.out.print("[error]: NIOserver发送信息失败");
            e.printStackTrace();
        }
    }
}

NIO 客户端代码

提示

  1. 类Client用于启动NIO客户端,传输参数只有IP、PORT
  2. 类ClientHandle用于NIO客户端请求服务端以及处理服务端的数返回数据
NIO客户端启动类
package com.ruider.customerNIO;

/**
 * Created by mahede on 2018/11/21.
 */
public class Client {
    private static final String IP_ADDRESS = "localhost";
    private static final int PORT = 1234;
    private static ClientHandle clientHandle;

    public static void start(){ start(IP_ADDRESS, PORT);}

    private synchronized static void start(String host, int port) {
        if(clientHandle != null) {
            clientHandle.stop();
        }
        clientHandle = new ClientHandle(IP_ADDRESS, PORT);
        new Thread(clientHandle, "Client").start();
    }

    //向服务器发送消息
    public static boolean sendMsg(String msg) throws Exception{
        if(msg.equals("q")) return false;
        clientHandle.sendMsg(msg);
        return true;
    }

}
客户端发送请求和处理返回数据类
package com.ruider.customerNIO;

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;

/**
 * Created by mahede on 2018/11/21.
 */
public class ClientHandle implements Runnable {

    private String ipAddress;
    private int port;
    private SocketChannel socketChannel;
    private Selector selector;
    private static boolean start;

    public ClientHandle(String host, int port) {
        try{
            this.ipAddress = host;
            this.port = port;
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            //socketChannel.connect(new InetSocketAddress(ipAddress, port));
            //socketChannel.register(selector, SelectionKey.OP_CONNECT);
            start = true;
            System.out.println("客户端启动");
        }catch (Exception e) {
            System.out.println("客户端启动失败");
            e.printStackTrace();
        }

    }

    public static void stop() { start = false;}

    public void sendMsg(String msg) throws Exception{
        socketChannel.register(selector, SelectionKey.OP_READ);
        doWrite(socketChannel, msg);
    }

    @Override
    public void run() {
        try{
            doConnect();
        }catch (Exception e){
            e.printStackTrace();
            System.exit(1);
        }

        while (start) {
            try {
                selector.select(1000);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey key = null;
                while (it.hasNext()) {
                    key = it.next();
                    it.remove();
                    try {
                        handleInput(key);
                    }
                    catch (Exception e) {
                        if(key != null){
                            key.cancel();
                            if(key.channel() != null){
                                key.channel().close();
                            }
                        }

                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
        try{
            if(selector != null) {
                selector.close();
            }
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void handleInput(SelectionKey key) {
        if(key.isValid()) {
            try{
                SocketChannel socketChannel = (SocketChannel)key.channel();
                if(key.isConnectable()){
                    if(socketChannel.finishConnect());
                    else System.exit(1);
                }
                if(key.isReadable()){
                    //创建ByteBuffer,并开辟一个1M的缓冲区
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    //读取请求码流,返回读取到的字节数
                    int readBytes = socketChannel.read(buffer);
                    //读取到字节,对字节进行编解码
                    if(readBytes>0){
                        //将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
                        buffer.flip();
                        //根据缓冲区可读字节数创建字节数组
                        byte[] bytes = new byte[buffer.remaining()];
                        //将缓冲区可读字节数组复制到新建的数组中
                        buffer.get(bytes);
                        String result = new String(bytes,"UTF-8");
                        System.out.println("客户端收到消息:" + result);
                    }
                    //没有读取到字节 忽略
//				else if(readBytes==0);
                    //链路已经关闭,释放资源
                    else if(readBytes<0){
                        key.cancel();
                        socketChannel.close();
                    }
                }
            }catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    //异步发送消息
    private void doWrite(SocketChannel channel,String request) throws IOException {
        //将消息编码为字节数组
        byte[] bytes = request.getBytes();
        //根据数组容量创建ByteBuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        //flip操作
        writeBuffer.flip();
        //发送缓冲区的字节数组
        channel.write(writeBuffer);
        //****此处不含处理“写半包”的代码
    }

    private void doConnect() throws IOException{
        if(socketChannel.connect(new InetSocketAddress(ipAddress, port)));
        else socketChannel.register(selector, SelectionKey.OP_CONNECT);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值