五.NIO应用-群聊实例

实例要求:

  1. 编写一个 NIO 群聊系统,实现服务器端和客户端之间的数据非阻塞简单通讯
  2. 实现多人群聊,单人群聊
  3. 服务器端:可以监测用户上线,离线,并实现消息转发功能
  4. 客户端:通过channel可以无阻塞发送消息给其它所有用户,同时可以接受其它用户发送的消息(有服务器转发得到)

服务端

public class GroupChatServer {

  //定义属性
  private final int PORT = 9999;
  private Selector selector;
  private ServerSocketChannel listenChannel;

  //构造方法,初始化属性
  private GroupChatServer() throws IOException {
    selector = Selector.open();
    listenChannel = ServerSocketChannel.open();
    //绑定端口
    listenChannel.socket().bind(new InetSocketAddress(PORT));
    //设置非堵塞
    listenChannel.configureBlocking(false);
    //将listenChannel注册到Selector上
    listenChannel.register(selector, SelectionKey.OP_ACCEPT);

  }

  private void listen() throws IOException {

    System.out.println("监听线程=" + Thread.currentThread().getName());
    //循环监听
    while (true) {
      //查询是否有事件
      int count = selector.select();
      if (count > 0) {
        //遍历得到selectionKey 集合
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator.hasNext()) {
          SelectionKey key = iterator.next();

          //监听到accept
          if (key.isAcceptable()) {
            SocketChannel sc = listenChannel.accept();
            sc.configureBlocking(false);
            //将该 sc 注册到selector
            sc.register(selector, SelectionKey.OP_READ);
            //提示
            System.out.println(sc.socket().getRemoteSocketAddress() + " 上线 ");
          }
          if (key.isReadable()) { //通道发送read事件,即通道是可读的状态
            //处理读 (专门写方法..)
            readData(key);
          }
          //当前的key 删除,防止重复处理
          iterator.remove();
        }
      } else {
        System.out.println(" 等待 ");
      }
    }
  }

  private void readData(SelectionKey key) {
    //获取关联的SocketChannel
    SocketChannel channel = null;
    try {
      channel = (SocketChannel) key.channel();
      //设置缓存
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      int count = channel.read(buffer);

      if (count > 0) {
        //把缓存区的数据转换为字符串
        String msg = new String(buffer.array());
        //输出该消息
        System.out.println("form 客户端: " + msg);
        //向其它的客户端转发消息(去掉自己)
        sendMsgToOtherClients(msg, channel);
      }

    } catch (IOException e) {
      try {
        System.out.println(channel.socket().getRemoteSocketAddress() + " 离线了..");
        //取消注册
        key.cancel();
        //关闭通道
        channel.close();
      } catch (IOException e2) {
        e2.printStackTrace();
      }
    }

  }

  private void sendMsgToOtherClients(String msg, SocketChannel clientSelf) throws IOException {
    System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName());
    Set<SelectionKey> keys = selector.keys();
    for (SelectionKey key : keys) {
      Channel toChannel = key.channel();

      //排除自己
      if (toChannel instanceof SocketChannel && toChannel != clientSelf) {
        SocketChannel other = (SocketChannel) toChannel;
        //把消息转成ByteBuffer
        ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
        //ByteBuffer写道通道里
        other.write(buffer);
      }
    }
  }

  public static void main(String[] args) throws IOException {
    GroupChatServer server = new GroupChatServer();
    server.listen();
  }
}

客户端

public class GroupChatClient {

  //定义属性
  private final String HOST = "127.0.0.1";
  private final int PORT = 9999;
  private Selector selector;
  private SocketChannel socketChannel;
  private String username;

  //构造器, 完成初始化工作
  public GroupChatClient() throws IOException {

    selector = Selector.open();
    //连接服务器
    socketChannel = socketChannel.open(new InetSocketAddress(HOST, PORT));
    //设置非阻塞
    socketChannel.configureBlocking(false);
    //将channel 注册到selector
    socketChannel.register(selector, SelectionKey.OP_READ);
    //得到username
    username = socketChannel.getLocalAddress().toString().substring(1);
    System.out.println(username + " is ok...");

  }

  private void sendMsg(String msg) throws IOException {
    msg = username + " 说 " + msg;
    socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
  }

  private void readMsg() throws IOException {
    //可用通道
    int count = selector.select();
    if (count > 0) {
      Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
      while (iterator.hasNext()) {
        SelectionKey key = iterator.next();
        if (key.isReadable()) {
          SocketChannel sc = (SocketChannel) key.channel();
          ByteBuffer buffer = ByteBuffer.allocate(1024);
          sc.read(buffer);
          //把buffer转成字符串
          System.out.println(new String(buffer.array()));
        }
      }
      iterator.remove();
    }
  }

  public static void main(String[] args) throws IOException {
    final GroupChatClient client = new GroupChatClient();
    new Thread() {
      @Override
      public void run() {

        while (true) {
          try {
            client.readMsg();
            Thread.currentThread().sleep(2000);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }
    }.start();

    //发送数据给服务器端
    Scanner scanner = new Scanner(System.in);

    while (scanner.hasNextLine()) {
      String s = scanner.nextLine();
      client.sendMsg(s);
    }
  }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值