NIO--SocketChanel和ServerSocketChannel实现简易群聊

  • 服务端代码

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;

public class NIOServer {
    //服务所用端口
    private static final int PORT=9999;
    //选择器
    private Selector selector;
    //服务端serverSocketChannel, 主要功能是处理事件
    private ServerSocketChannel serverSocketChannel;
    //初始化服务端
    {
        try {
            //创建选择器
            selector=Selector.open();
            //创建serverSocketChannel
            serverSocketChannel=ServerSocketChannel.open();
            //设置为非阻塞式
            serverSocketChannel.configureBlocking(false);
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            //把serverSocketChannel注册到选择器,使选择器监听accept事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //事件监听:accept和read事件
    public void listener(){
        while (true) {
            try {
                System.out.println("开始监听事件");
                //当没有事件发生时会阻塞在此位置,也可设置不阻塞或阻塞超时时间
                selector.select();
                //当有事件发生时,获取发生事件的SelectionKey集合,通过SelectionKey可以获取socketChannel通道和发生事件的类型等
                Iterator iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    System.out.print("监听到事件:");
                    SelectionKey selectionKey = iterator.next();
                    //accept事件处理
                    if (selectionKey.isAcceptable()) {
                        System.out.println("连接事件");
                        //使用serverSocketChannel分配一个代表客户端连接的socketChannel
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        //设置为非阻塞式
                        socketChannel.configureBlocking(false);
                        //注册到选择器上,并让选择器监听read事件
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    //read事件处理
                    if (selectionKey.isReadable()) {
                        System.out.println("消息读取事件");
                        //获取事件发生、代表客户端连接的socketChannel
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                        //读取通道的数据到缓存,用来记录日志
                        ByteBuffer b = ByteBuffer.allocate(1024);
                        int read = socketChannel.read(b);
                        String msg=new String(b.array(), 0, read);
                        String addr = socketChannel.getRemoteAddress().toString().substring(11)+"说:";
                        System.out.println("服务器群发消息:"+addr+msg);

                        //群发消息(不包含自己)
                        sendMsgToAll(selectionKey,msg);
                    }
                    //移出处理完事件的selectionKey
                    iterator.remove();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    //群发消息(不包含自己)
    private void sendMsgToAll(SelectionKey self,String msg){
        System.out.println("一共有"+selector.keys().size()+"个连接");
        try {
            SocketChannel socketChannel = (SocketChannel) self.channel();
            String addr = socketChannel.getRemoteAddress().toString().substring(11)+"说:";
            msg=addr+msg;

            String finalMsg = msg;

            selector.keys().stream().filter(selectionKey -> self!=selectionKey).forEach(selectionKey->{
                try {
                    //群发消息(ServerSocketChannel除外)
                    if(selectionKey.channel() instanceof SocketChannel) {
                        //获取代表客户端连接的socketChannel通道
                        SocketChannel socketChannel1 = (SocketChannel) selectionKey.channel();
                        //把缓存内的数据写入到socketChannel通道
                        socketChannel1.write(ByteBuffer.wrap(finalMsg.getBytes()));
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        NIOServer server=new NIOServer();
        server.listener();
    }
}
  • 客户端代码

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

public class NIOClient {
    // 客户端socketChannel
    private SocketChannel socketChannel;
    {
        try {
            //创建socketChannel
            socketChannel=SocketChannel.open();
            //设置非阻塞式
            socketChannel.configureBlocking(false);
            InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9999);

            // 创建连接
            if(!socketChannel.connect(inetSocketAddress)){
                while (!socketChannel.finishConnect()){
                    System.out.println("客户端不会阻塞,可以做其他工作");
                }
            }
            //连接成功后开启一个线程定时2秒接收服务器转发的消息
            receiveMsg();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 收消息线程
    private void receiveMsg(){
        new Thread(()->{
            System.out.println("开始收取信息...");
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (true) {
                try {
                    //把通道的数据写入缓存buffer
                    int read = socketChannel.read(buffer);
                    if(read>0)
                        System.out.println(new String(buffer.array(),0,read));

                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    //清空缓存buffer
                    buffer.clear();
                }
            }
        }).start();
    }
    //发送消息
    public void sendMsg(String msg){
        try {
            //发送消息
            socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        NIOClient client=new NIOClient();

        // 循环发送消息
        Scanner scanner=new Scanner(System.in);
        while (scanner.hasNextLine()) {
            try {
                client.sendMsg(scanner.nextLine());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

 

你可能感兴趣的:(BIO,NIO,Netty)