Java Socket编程(非阻塞多线程,NIO)

服务端:

服务器Server类

public class Server implements Runnable {
    private int port;
    private volatile boolean stop;
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;

    public Server(int port){
        this.port = port;
    }

    public void init(){
        try {
            //打开一个选择器
            selector = Selector.open();
            //打开一个Server-Socket监听通道
            serverSocketChannel = ServerSocketChannel.open();
            //设置该通道为非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(port));
            //将通道注册在选择器上面,并将准备连接状态作为通道订阅时间
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            stop = false;
            System.out.println("服务器已经启动,端口号:" + port);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public void run() {
        init();
        while (!stop){
            try {
                //无论是否有读写事件发生,selector每隔1s被唤醒一次
                selector.select(1000);
                Set selectionKeys = selector.selectedKeys();
                Iterator iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    //判断是否准备好接收新进入的连接
                    if(selectionKey.isAcceptable()){
                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                        //通过ServerSocketChannel的accept()创建SocketChannel实例
                        //完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        //设置为非阻塞
                        socketChannel.configureBlocking(false);
                        //在选择器注册,并订阅读事件
                        socketChannel.register(selector,SelectionKey.OP_READ);
                    }
                    if(selectionKey.isReadable()){
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        //创建byteBuffer,并开辟一个1M的缓冲区
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        //读取请求码流,返回读取到的字节数
                        int readBytes = socketChannel.read(byteBuffer);
                        //判断客户端是否断开
                        if(readBytes < 0){
                            selectionKey.cancel();
                            socketChannel.close();
                            return;
                        }
                        //读取到字节,对字节进行编解码
                        if(readBytes>0){
                            //将缓冲区从写模式切换到读模式
                            byteBuffer.flip();
                            //根据缓冲区可读字节数创建字节数组
                            byte[] bytes = new byte[byteBuffer.remaining()];
                            //向缓冲区读数据到字节数组
                            byteBuffer.get(bytes);
                            String expression = new String(bytes,"UTF-8");
                            System.out.println("服务器收到消息:"+expression);
                        }
                    }
                    iterator.remove();
                }
                selectionKeys.clear();
            }catch (IOException e){
                e.printStackTrace();
            }
        }

        //selector关闭后会自动释放里面管理的资源
        if(selector != null){
            try {
                selector.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

客户端:

客户端Client类

public class Client implements Runnable {

    private String host;
    private int port;
    private Selector selector;
    private SocketChannel socketChannel;
    private volatile boolean stop;
    private String name;

    public Client(int port,String name){
        this("localhost",port,name);
    }

    public Client(String host,int port,String name){
        this.host = host;
        this.port = port;
        this.name = name;
    }

    public void init(){
        try {
            //打开一个选择器
            selector = Selector.open();
            //打开一个Socket监听通道
            socketChannel = SocketChannel.open();
            //设置该通道为非阻塞模式
            socketChannel.configureBlocking(false);
            //在非阻塞模式下,该方法在建立连接之前就会返回结果了,后续为了确认连接是否建立成功,可以调用finishConnect()
            socketChannel.connect(new InetSocketAddress(host,port));
            //订阅连接事件
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            stop = false;
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public void run() {
        init();
        int i = 0;
        while (!stop){
            try {
                //无论是否有读写事件发生,selector每隔1s被唤醒一次
                selector.select(1000);
                Set selectionKeys = selector.selectedKeys();
                Iterator iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    //判断是否连接到服务器
                    if(selectionKey.isConnectable()){
                        //判断连接是否建立成功
                        if(socketChannel.finishConnect()){
                            sendMsg(name+" Connect Success!");
                            socketChannel.register(selector,SelectionKey.OP_WRITE);
                        }
                    }
                    if(selectionKey.isWritable()){
                        sendMsg(name+" is saying \"Hello World\"!"+i++);
                        Thread.sleep(1000);
                    }
                    iterator.remove();
                }
                selectionKeys.clear();
            }catch (ConnectException e){
                System.out.println("连接失败!");
                return;
            }catch (IOException e){
                e.printStackTrace();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    public void sendMsg(String expression) throws IOException{
        byte[] bytes = expression.getBytes();
        ByteBuffer byteBuffer = ByteBuffer.allocate(bytes.length);
        byteBuffer.put(bytes);
        //翻转缓冲区,执行的操作:
        //1.将limit的位置设为position之后的一个位置
        //2.将position的位置重置为0
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        //清空缓冲区
        byteBuffer.clear();
    }
}

测试过程:

测试类TestNio

public class TestNio {
    public static void main(String[] args) {
        Thread server = new Thread(new Server(10086));
        Thread client1 = new Thread(new Client(10086,"ONE"));
        Thread client2 = new Thread(new Client(10086,"TWO"));
        server.start();
        client1.start();
        client2.start();
    }
}

测试结果:

服务器已经启动,端口号:10086
服务器收到消息:TWO Connect Success!
服务器收到消息:ONE Connect Success!
服务器收到消息:ONE is saying "Hello World"!0
服务器收到消息:TWO is saying "Hello World"!0
服务器收到消息:ONE is saying "Hello World"!1
服务器收到消息:TWO is saying "Hello World"!1
服务器收到消息:ONE is saying "Hello World"!2
服务器收到消息:TWO is saying "Hello World"!2
服务器收到消息:ONE is saying "Hello World"!3
服务器收到消息:TWO is saying "Hello World"!3
服务器收到消息:ONE is saying "Hello World"!4
服务器收到消息:TWO is saying "Hello World"!4
服务器收到消息:ONE is saying "Hello World"!5
服务器收到消息:TWO is saying "Hello World"!5
服务器收到消息:ONE is saying "Hello World"!6
服务器收到消息:TWO is saying "Hello World"!6
服务器收到消息:ONE is saying "Hello World"!7
服务器收到消息:TWO is saying "Hello World"!7
服务器收到消息:ONE is saying "Hello World"!8
服务器收到消息:TWO is saying "Hello World"!8
服务器收到消息:ONE is saying "Hello World"!9
服务器收到消息:TWO is saying "Hello World"!9
服务器收到消息:ONE is saying "Hello World"!10
服务器收到消息:TWO is saying "Hello World"!10

你可能感兴趣的:(java,socket,NIO)