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

26 篇文章 1 订阅
13 篇文章 0 订阅
  • 服务端代码

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<SelectionKey> 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();
            }
        }
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值