【Netty整理03-NIO】Java 实现 NIO demo

自作多情 提交于 2020-02-04 23:48:17

jdk提供的NIO使用:

概览:https://blog.csdn.net/the_fool_/article/details/83000648

 

博主抄写了网上的demo,略作修改与调整,原文链接:

Demo01:抄写地址忘记了。。。

1、服务端代码:

package com.test3;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;

/**
 * NIO服务端代码
 * @author http://
 * @author ZX
 * 监听客户端连接,接收、发送消息
 *
 */
public class AServer {
    public static  void  main(String[]args)throws Exception{
        System.out.println("=================");
        //创建选择器
        Selector selector = Selector.open();
        //创建打开服务端的监听
        ServerSocketChannel sChannel = ServerSocketChannel.open();
        //绑定本地地址
        sChannel.socket().bind(new InetSocketAddress(9999));
        //设置非阻塞模式
        sChannel.configureBlocking(false);
        //将通道绑定到选择器,非阻塞通道才能注册到选择器,第二个参数好像是方式或者操作吧
        sChannel.register(selector, SelectionKey.OP_ACCEPT);

        TcpProtocal protocol = new TcpProtocal();
        //循环监听等待
        while (true){
            //
            if(selector.select(3000)==0){
                System.out.println("继续等待");
                continue;
            }
            //间听到的可操作集合
            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
            while (keyIter.hasNext()){
                SelectionKey key = keyIter.next();
                //这是干嘛的?获取下下个?一点用没有啊???
                /*  SelectionKey key1;
                if(keyIter.hasNext()){
                    key1 = keyIter.next();
                }*/
                try {
                    //如果有客户端连接请求
                    if(key.isAcceptable()){
                        protocol.handleAccept(key);
                    }
                    //如果有数据发送
                    if(key.isReadable()){
                        protocol.handleRead(key);
                        protocol.handleWrite(key);
                    }
                    //是否有效,是否可发送给客户端
                    if(key.isValid()&&key.isWritable()){
                        //protocol.handleWriteMsg(key,"我服务端在这里说点事情");
                    }
                }catch (IOException e){
                    keyIter.remove();
                    e.printStackTrace();
                    continue;
                }
                //删除处理过的键
                keyIter.remove();



            }

        }

    }
}

 

2、协议:

 

package com.test3;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

/**
 * 定义一个协议传输信息
 */
public class TcpProtocal {
    private int bufferSize=1024;
    /** 接受一个SocketChannel处理*/
    public void handleAccept(SelectionKey key) throws Exception{
        //返回创建此键的通道,接收客户端建立连接的请求,并返回socketChannel对象
        SocketChannel clientChannel=((ServerSocketChannel)key.channel()).accept();
        //设置非阻塞
        clientChannel.configureBlocking(false);
        //注册到selector
        clientChannel.register(key.selector(),SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));

    }
    /** 从一个一个SocketChannel读取信息*/
    public void handleRead(SelectionKey key) throws Exception{
        //获得与客户端通信的通道
        SocketChannel clientChannel=(SocketChannel)key.channel();
        //得到并清空缓冲区并清空缓冲区
        ByteBuffer buffer= (ByteBuffer)key.attachment();
        buffer.clear();
        //读取信息获得的字节数
        int byteRead = clientChannel.read(buffer);
        if(byteRead==-1){
            clientChannel.close();
        }else{
            //将缓冲区准备为数据传状态
            buffer.flip();
            //将字节转换为UTF-16
            String receivedStr = Charset.forName("UTF-8").newDecoder().decode(buffer).toString();
            System.out.println("接收到来自"+clientChannel.socket().getRemoteSocketAddress()+"信息"+receivedStr);
            key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
            /*String sendStr ="已收到信息";
            buffer=ByteBuffer.wrap(sendStr.getBytes("UTF-8"));
            clientChannel.write(buffer);
            //设置为下依稀读取写入做准备
            key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);*/
        }


    }
    /** 向一个一个SocketChannel写入信息*/
    public void handleWrite(SelectionKey key) throws Exception{
        handleWriteMsg(key,null);

    }

    /** 向一个一个SocketChannel写入信息*/
    public void handleWriteMsg(SelectionKey key,String msg) throws Exception{
        if(msg==null||"".equals(msg)){
            msg="服务器主动说:已收到建立请求消息";
        }
        //获得与客户端通信的通道
        SocketChannel clientChannel=(SocketChannel)key.channel();
        //得到并清空缓冲区并清空缓冲区
        ByteBuffer buffer= (ByteBuffer)key.attachment();
        buffer.clear();
        String sendStr =msg;
        buffer=ByteBuffer.wrap(sendStr.getBytes("UTF-8"));
        clientChannel.write(buffer);
        //设置为下依稀读取写入做准备
        key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);

    }
}

 

3、客户端处理类:

 

package com.test3;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

public class TcpClientReadThread implements Runnable{
    private Selector selector;

    public TcpClientReadThread(Selector selector){
        this.selector=selector;
        new Thread(this).start();
    }

    public void run() {
        try {
            //select()方法只能使用一次,用过之后就会删除,每个连接到服务器的选择器都是独立的
            while (selector.select()>0){
                //遍历所有可以IO操作做的Channel对应的selectionKey
                for(SelectionKey sk:selector.selectedKeys()){
                    //如果数据可读
                    if(sk.isReadable()){
                        //使用NIO读取Channel中可读数据
                        //获取通道信息
                        SocketChannel sc =  (SocketChannel)sk.channel();
                        //创建缓冲区
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        //读取数据到缓冲区
                        sc.read(buffer);
                        //吊用此方法为读取写入做准备
                        buffer.flip();

                        String receiveStr = Charset.forName("UTF-8").newDecoder().decode(buffer).toString();
                        System.out.println("收到服务器"+sc.socket().getRemoteSocketAddress()+"信息"+receiveStr);
                        //为下一次读取做准备
                        sk.interestOps(SelectionKey.OP_READ);
                        //删除正在处理的selectionKey
                        selector.selectedKeys().remove(sk);


                    }

                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

 

4、客户端代码:

 

package com.test3;

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.Scanner;

public class AClient {

    private Selector selector;
    private SocketChannel socketChannel;

    private String hostIp;
    private  int hostPort;

    public AClient(String hostIp,int hostPort)throws Exception{
        this.hostIp=hostIp;
        this.hostPort=hostPort;
        init();

    }
    public void init()throws Exception{
        socketChannel=SocketChannel.open(new InetSocketAddress(hostIp,hostPort));
        socketChannel.configureBlocking(false);
        //打开并注册选择器信道、
        selector= Selector.open();
        socketChannel.register(selector,SelectionKey.OP_READ);
        //启动读取线程
        new TcpClientReadThread(selector);


    }

    /**
     *发送字符串到服务器
     */
    public void sendMsg(String msgg)throws Exception{
        ByteBuffer writeBuffer = ByteBuffer.wrap(msgg.getBytes("UTF-8"));
        socketChannel.write(writeBuffer);
    }
    static AClient aClient;
    static  boolean mFlag=true;
    public static void main(String[]args)throws Exception{
        aClient=new AClient("127.0.0.1",9999);


        new Thread(){
            @Override
            public void run() {
                try {
                    aClient.sendMsg("客户端======");
                    while (mFlag){
                        Scanner sc = new Scanner(System.in);
                        String next = sc.next();
                        aClient.sendMsg(next);
                    }
                }catch (Exception e){
                    mFlag=false;
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

=================================================================================================

DEMO02:这个看起来更清楚一些,重新补充如下demo:

摘抄地址:

作者:anxpp
来源:CSDN
原文:https://blog.csdn.net/anxpp/article/details/51512200?utm_source=copy

Server:

package javanio;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
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;
/**NIO 步骤*/

/**            打开ServerSocketChannel,监听客户端连接
            绑定监听端口,设置连接为非阻塞模式
            创建Reactor线程,创建多路复用器并启动线程
            将ServerSocketChannel注册到Reactor线程中的Selector上,监听ACCEPT事件
            Selector轮询准备就绪的key
            Selector监听到新的客户端接入,处理新的接入请求,完成TCP三次握手,简历物理链路
            设置客户端链路为非阻塞模式
            将新接入的客户端连接注册到Reactor线程的Selector上,监听读操作,读取客户端发送的网络消息
            异步读取客户端消息到缓冲区
            对Buffer编解码,处理半包消息,将解码成功的消息封装成Task
            将应答消息编码为Buffer,调用SocketChannel的write将消息异步发送给客户端
        ---------------------
        作者:anxpp
        来源:CSDN
        原文:https://blog.csdn.net/anxpp/article/details/51512200?utm_source=copy
        版权声明:本文为博主原创文章,转载请附上博文链接!*/
public class Server {
    private static int DEFAULT_PORT = 8081;
    private static ServerHandle serverHandle;
    public static void start(){
        start(DEFAULT_PORT);
    }
    public static synchronized void start(int port){
        if(serverHandle!=null){
            serverHandle.stop();
        }

        serverHandle = new ServerHandle(port);
        new Thread(serverHandle,"Server").start();
    }
    public static void main(String[] args){
        start();
    }


}

class ServerHandle implements Runnable{
    private Selector selector;
    private ServerSocketChannel serverChannel;
    private volatile boolean started;

    /**
     * 构造方法
     * @param port 指定要监听的端口号
     */
    public ServerHandle(int port) {
        try{
            //创建选择器
            selector = Selector.open();
            //打开ServerSocketChannel,监听客户端连接
            serverChannel = ServerSocketChannel.open();
            //如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
            serverChannel.configureBlocking(false);
            //绑定端口 backlog设为1024
            serverChannel.socket().bind(new InetSocketAddress(port),1024);
            //监听客户端连接请求
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            //标记服务器已开启
            started = true;
            System.out.println("SERVER START,PORT:" + port);
        }catch(IOException e){
            e.printStackTrace();
            System.exit(1);
        }
    }
    public void stop(){
        started = false;
    }


    @Override
    public void run() {
        //循环遍历selector
        while(started){
            try{
                //无论是否有读写事件发生,selector每隔1s被唤醒一次
                selector.select(1000);
                //阻塞,只有当至少一个注册的事件发生的时候才会继续.
//				selector.select();
                //返回已此通道已准备就绪的键集,已选择始终是键集的一个子集
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> it = keys.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(Throwable t){
                t.printStackTrace();
            }
        }
        //selector关闭后会自动释放里面管理的资源
        if(selector != null)
            try{
                selector.close();
            }catch (Exception e) {
                e.printStackTrace();
            }


    }

    private void handleInput(SelectionKey key) throws IOException{
        if(key.isValid()){
            //处理新接入的请求消息
            if(key.isAcceptable()){
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                //通过ServerSocketChannel的accept创建SocketChannel实例
                //完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
                SocketChannel sc = ssc.accept();
                //设置为非阻塞的
                sc.configureBlocking(false);
                //注册为读
                sc.register(selector, SelectionKey.OP_READ);
            }
            //读消息
            if(key.isReadable()){
                SocketChannel sc = (SocketChannel) key.channel();
                //创建ByteBuffer,并开辟一个1M的缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //读取请求码流,返回读取到的字节数
                int readBytes = sc.read(buffer);
                //读取到字节,对字节进行编解码
                if(readBytes>0){
                    //将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
                    buffer.flip();
                    //根据缓冲区可读字节数创建字节数组
                    byte[] bytes = new byte[buffer.remaining()];
                    //将缓冲区可读字节数组复制到新建的数组中
                    buffer.get(bytes);
                    String expression = new String(bytes,"UTF-8");
                    System.out.println("SERVER GET MSSG:" + expression);
                    //处理数据
                    String result = null;
                    try{
                        result = Calculator.cal(expression).toString();
                    }catch(Exception e){
                        result = "CAL ERR:" + e.getMessage();
                    }
                    //发送应答消息
                    doWrite(sc,result);
                }
                //没有读取到字节 忽略
//				else if(readBytes==0);
                //链路已经关闭,释放资源
                else if(readBytes<0){
                    key.cancel();
                    sc.close();
                }
            }
        }
    }
    //异步发送应答消息
    private void doWrite(SocketChannel channel,String response) throws IOException{
        //将消息编码为字节数组
        byte[] bytes = response.getBytes();
        //根据数组容量创建ByteBuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        //flip操作
        writeBuffer.flip();
        //发送缓冲区的字节数组
        channel.write(writeBuffer);
        //****此处不含处理“写半包”的代码
    }



    void test(){

    }

}
 class Calculator {
    private final static ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
    public static Object cal(String expression) throws ScriptException {
        return jse.eval(expression);
    }
}

Client:

package javanio;

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;

public class Client {

    private static String DEFAULT_HOST = "127.0.0.1";
    private static int DEFAULT_PORT = 8081;
    private static ClientHandle clientHandle;
    public static void start(){
        start(DEFAULT_HOST,DEFAULT_PORT);
    }
    public static synchronized void start(String ip,int port){
        if(clientHandle!=null)
            clientHandle.stop();
        clientHandle = new ClientHandle(ip,port);
        new Thread(clientHandle,"Server").start();
    }
    //向服务器发送消息
    public static boolean sendMsg(String msg) throws Exception{
        if(msg.equals("q")) return false;
        clientHandle.sendMsg(msg);
        return true;
    }
    public static void main(String[] args){
        start();
    }


}
class ClientHandle implements Runnable{
    private String host;
    private int port;
    private Selector selector;
    private SocketChannel socketChannel;
    private volatile boolean started;

    public ClientHandle(String ip,int port) {
        this.host = ip;
        this.port = port;
        try{
            //创建选择器
            selector = Selector.open();
            //打开监听通道
            socketChannel = SocketChannel.open();
            //如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
            socketChannel.configureBlocking(false);//开启非阻塞模式
            started = true;
        }catch(IOException e){
            e.printStackTrace();
            System.exit(1);
        }
    }
    public void stop(){
        started = false;
    }
    @Override
    public void run() {
        try{
            doConnect();
        }catch(IOException e){
            e.printStackTrace();
            System.exit(1);
        }
        //循环遍历selector
        while(started){
            try{
                //无论是否有读写事件发生,selector每隔1s被唤醒一次
                selector.select(1000);
                //阻塞,只有当至少一个注册的事件发生的时候才会继续.
//				selector.select();
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> it = keys.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);
            }
        }
        //selector关闭后会自动释放里面管理的资源
        if(selector != null)
            try{
                selector.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
    }
    private void handleInput(SelectionKey key) throws IOException{
        if(key.isValid()){
            SocketChannel sc = (SocketChannel) key.channel();
            if(key.isConnectable()){
                if(sc.finishConnect());
                else System.exit(1);
            }
            //读消息
            if(key.isReadable()){
                //创建ByteBuffer,并开辟一个1M的缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //读取请求码流,返回读取到的字节数
                int readBytes = sc.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("CLIENT GET MSG:" + result);
                }
                //没有读取到字节 忽略
//				else if(readBytes==0);
                //链路已经关闭,释放资源
                else if(readBytes<0){
                    key.cancel();
                    sc.close();
                }
            }
        }
    }
    //异步发送消息
    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(host,port)));
        else socketChannel.register(selector, SelectionKey.OP_CONNECT);
    }
    public void sendMsg(String msg) throws Exception{
        socketChannel.register(selector, SelectionKey.OP_READ);
        doWrite(socketChannel, msg);
    }


}

Test:

package javanio;

import java.util.Scanner;

public class Test {
    public static void main(String[] args) throws Exception{
        //运行服务器
        Server.start();
        //避免客户端先于服务器启动前执行代码
        Thread.sleep(100);
        //运行客户端
        Client.start();
        while(Client.sendMsg(new Scanner(System.in).nextLine()));
    }


}

 

demo3:https://blog.csdn.net/Howinfun/article/details/81283721

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!