五.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);
    }
  }

}

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