一站式学习Java网络编程 全面理解BIO_NIO_AIO,学习手记(八)

大家好,我是方圆


1. AIO模型分析

在这里插入图片描述

  • AsynchronousServerSocket:它属于一个AsynchronousChannelGroup,这个通道组,其实是被多个异步通道共享的资源群组,这里边我们之前提到过,有一个非常重要的资源:线程池,系统会利用线程池中的线程,来处理一些handler请求。系统利用这个资源组还为我们做了很多的事情,包括它能在数据准备好的时候通知我们和利用handler做一些异步的操作。当我们在创建AsynchronousServerSocket时(open()),我们可以自定义一个通道组,当然我们不传参的时候,系统会默认给我们一个群组。

在这里插入图片描述

  • 当客户端请求与服务器建立连接时,系统会异步的调用AcceptHandler来处理连接请求,成功建立连接后,会返回一个AsynchronousSocketChannel对象,每个对象还会有一个ClientHandler来处理读写请求,在请求处理的过程中,并不是在主线程中完成的,而是通道组利用线程池资源,在不同的线程中完成异步处理。

2. 聊天室分析

2.1 服务器端

2.1.1 字段

在这里插入图片描述

2.1.2 主方法

在这里插入图片描述

2.1.3 AcceptHandler

在这里插入图片描述

2.1.4 ClientHandler(处理读写请求)

在这里插入图片描述

2.1.5 添加和删除用户

在这里插入图片描述

2.1.6 接收和转发方法

在这里插入图片描述

扫描二维码关注公众号,回复: 11824634 查看本文章

2.2 客户端

客户端中使用的Future来处理异步请求,非常简单

2.2.1 主方法

在这里插入图片描述

2.2.2 发送消息

在这里插入图片描述

2.2.3 用户的输入线程

在这里插入图片描述


3. 测试结果

  • 服务器端显示
    在这里插入图片描述

4. 完整代码

4.1 服务器端

package server;

import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ChatServer {
    
    

    private static final String LOCALHOST = "localhost";
    private static final int DEFAULT_PORT = 8888;
    private static final String QUIT = "quit";
    private static final int BUFFER = 1024;

    private AsynchronousServerSocketChannel serverChannel;
    private AsynchronousChannelGroup asynchronousChannelGroup;
    private List<ClientHandler> connectedClients;
    private Charset charset = StandardCharsets.UTF_8;
    private int port;

    public ChatServer(int port) {
    
    
        this.port = port;
        connectedClients = new ArrayList<>();
    }

    public ChatServer() {
    
    
        this(DEFAULT_PORT);
    }

    public void start(){
    
    
        try {
    
    
            //自定义ChannelGroup
            ExecutorService executorService = Executors.newFixedThreadPool(10);
            asynchronousChannelGroup = AsynchronousChannelGroup.withThreadPool(executorService);

            serverChannel = AsynchronousServerSocketChannel.open(asynchronousChannelGroup);
            serverChannel.bind(new InetSocketAddress(LOCALHOST,port));
            System.out.println("服务器已经启动成功,随时等待客户端连接...");

            while (true){
    
    
                serverChannel.accept(null,new AcceptHandler());

                System.in.read();
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            close(serverChannel);
        }
    }

    private class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel,Object> {
    
    
        @Override
        public void completed(AsynchronousSocketChannel clientChannel, Object attachment) {
    
    
            if(serverChannel.isOpen())
                serverChannel.accept(null,this);

            if(clientChannel != null && clientChannel.isOpen()){
    
    
                ClientHandler clientHandler = new ClientHandler(clientChannel);

                ByteBuffer buffer = ByteBuffer.allocate(BUFFER);
                addClient(clientHandler);
                clientChannel.read(buffer,buffer,clientHandler);
            }

        }

        @Override
        public void failed(Throwable exc, Object attachment) {
    
    
            System.out.println("连接失败:" + exc.getMessage());
        }
    }

    private class ClientHandler implements CompletionHandler<Integer,ByteBuffer>{
    
    

        private AsynchronousSocketChannel clientChannel;

        public ClientHandler(AsynchronousSocketChannel clientChannel) {
    
    
            this.clientChannel = clientChannel;
        }

        public AsynchronousSocketChannel getClientChannel() {
    
    
            return clientChannel;
        }

        @Override
        public void completed(Integer result, ByteBuffer buffer) {
    
    
            if(buffer != null){
    
    
                //buffer不为空的时候,这要执行的是read之后的回调方法
                if(result <= 0){
    
    
                    //客户端异常,将客户端从连接列表中移除
                    removeClient(this);
                }else{
    
    
                    buffer.flip();
                    String fwdMsg = receive(buffer);
                    System.out.println(getClientName(clientChannel) + fwdMsg);
                    forwardMsg(clientChannel,fwdMsg);
                    buffer.clear();

                    if(readyToQuit(fwdMsg)){
    
    
                        removeClient(this);
                    }else {
    
    
                        clientChannel.read(buffer,buffer,this);
                    }
                }
            }
        }

        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {
    
    
            System.out.println("读写操作失败:" + exc.getMessage());
        }
    }

    private synchronized void addClient(ClientHandler clientHandler) {
    
    
        connectedClients.add(clientHandler);
        System.out.println(getClientName(clientHandler.getClientChannel()) + "已经连接");
    }

    private synchronized void removeClient(ClientHandler clientHandler) {
    
    
        AsynchronousSocketChannel clientChannel = clientHandler.getClientChannel();
        connectedClients.remove(clientHandler);
        System.out.println(getClientName(clientChannel) + "已经断开连接");
        close(clientChannel);
    }

    private void close(Closeable closeable){
    
    
        try {
    
    
            closeable.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    private boolean readyToQuit(String msg){
    
    
        return QUIT.equals(msg);
    }

    private synchronized String receive(ByteBuffer buffer) {
    
    
        return String.valueOf(charset.decode(buffer));
    }

    private synchronized void forwardMsg(AsynchronousSocketChannel clientChannel,String fwdMsg) {
    
    
        for (ClientHandler connectedHandler : connectedClients) {
    
    
            AsynchronousSocketChannel client = connectedHandler.getClientChannel();
            if(!client.equals(clientChannel)){
    
    
                //注意这个try,catch是自己加的
                try {
    
    
                    //将消息存入缓存区中
                    ByteBuffer buffer = charset.encode(getClientName(client) + fwdMsg);
                    //写给每个客户端
                    client.write(buffer,null,connectedHandler);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }
        }

    }

    private String getClientName(AsynchronousSocketChannel clientChannel) {
    
    
        int port = -1;
        try {
    
    
            InetSocketAddress remoteAddress = (InetSocketAddress) clientChannel.getRemoteAddress();
            port = remoteAddress.getPort();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        return "客户端[" + port + "]:";
    }

    public static void main(String[] args) {
    
    
        ChatServer chatServer = new ChatServer();
        chatServer.start();
    }
}

4.2 客户端

package client;

import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class ChatClient {
    
    

    private static final String LOCALHOST = "localhost";
    private static final int DEFAULT_PORT = 8888;
    private final String QUIT = "quit";
    private final int BUFFER = 1024;

    private String host;
    private int port;
    private AsynchronousSocketChannel clientChannel;
    private Charset charset = StandardCharsets.UTF_8;

    public ChatClient() {
    
    
        this(LOCALHOST,DEFAULT_PORT);
    }

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

    public void start(){
    
    
        try {
    
    
            clientChannel = AsynchronousSocketChannel.open();
            Future<Void> connect = clientChannel.connect(new InetSocketAddress(host, port));
            connect.get();
            System.out.println("与服务已成功建立连接");
            new Thread(new UserInputHandler(this)).start();

            ByteBuffer buffer = ByteBuffer.allocate(BUFFER);
            while (clientChannel.isOpen()){
    
    
                Future<Integer> read = clientChannel.read(buffer);
                int result = read.get();
                if(result <= 0){
    
    
                    //这里是,当我们输入quit时,在服务器端会自动将我们移除
                    //所以这里关闭就好了
                    close(clientChannel);
                }else {
    
    
                    buffer.flip();
                    String msg = String.valueOf(charset.decode(buffer));
                    System.out.println(msg);
                    buffer.clear();
                }
            }
        } catch (IOException | InterruptedException | ExecutionException e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            close(clientChannel);
        }
    }

    public void sendMsg(String msg){
    
    
        if(msg.isEmpty()){
    
    
            return;
        }else {
    
    
            ByteBuffer buffer = charset.encode(msg);
            Future<Integer> write = clientChannel.write(buffer);
            try {
    
    
                write.get();
            } catch (InterruptedException | ExecutionException e) {
    
    
                e.printStackTrace();
            }
        }
    }

    private void close(Closeable closeable){
    
    
        try {
    
    
            closeable.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    public boolean readyToQuit(String msg){
    
    
        return QUIT.equals(msg);
    }

    public static void main(String[] args) {
    
    
        ChatClient chatClient = new ChatClient();
        chatClient.start();
    }
}
package client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class UserInputHandler implements Runnable{
    
    
    private ChatClient client;

    public UserInputHandler(ChatClient client) {
    
    
        this.client = client;
    }

    @Override
    public void run() {
    
    
        BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));

        while (true){
    
    
            try {
    
    
                String msg = consoleReader.readLine();
                client.sendMsg(msg);

                if(client.readyToQuit(msg)){
    
    
                    System.out.println("成功退出聊天室");
                    break;
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

三个聊天室整完啦!

猜你喜欢

转载自blog.csdn.net/qq_46225886/article/details/107568561