服务器端

编写NIO服务端:
1、创建 SeverSockerChannel 对象
2、将 ServerSocketChannel 绑定到本机端口
3、将 ServerSocketChannel 设置为非阻塞模式
4、创建 Selector 选择器
5、将 ServerSocketChannel 注册到 Selector 选择器上并监听 SelectionKey.OP_ACCEPT 连接事件
6、轮询 selector 选择器,直到有事件准备就绪
7、对准备就绪的事件进行处理

  1. public class NioServer {
  2. public void start() throws IOException {
  3. // 1.创建Selector
  4. Selector selector = Selector.open();
  5. // 2.创建ServerSocketChannel
  6. ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
  7. // 3.绑定本地端口
  8. serverSocketChannel.socket().bind(new InetSocketAddress(10000));
  9. // 4.设置非阻塞模式
  10. serverSocketChannel.configureBlocking(false);
  11. // 5.将serverSocketChannel注册到Selector,并监听连接事件
  12. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
  13. // 6.循环监听并处理事件
  14. while (true) {
  15. // 阻塞方法:获取已经就绪的事件
  16. int readyChannels = selector.select();
  17. // 没有准备就绪的事件,跳过本轮循环
  18. if (readyChannels == 0) {
  19. continue;
  20. }
  21. // 已经就绪的channel集合
  22. Set<SelectionKey> selectionKeys = selector.selectedKeys();
  23. // 获取迭代器
  24. Iterator<SelectionKey> iterator = selectionKeys.iterator();
  25. while (iterator.hasNext()) {
  26. // 获取一个selectKey实例
  27. SelectionKey selectionKey = iterator.next();
  28. // 移除selectKey实例,避免多次处理
  29. iterator.remove();
  30. // 7.根据就绪状态调用相应的处理逻辑
  31. // 连接事件就绪
  32. if (selectionKey.isAcceptable()) {
  33. acceptHandler(serverSocketChannel, selector);
  34. }
  35. // 可读事件就绪
  36. if (selectionKey.isReadable()) {
  37. readHandler(selectionKey, selector);
  38. }
  39. }
  40. }
  41. }
  42. private void acceptHandler(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
  43. // 获取相应客户端的SocketChannel
  44. SocketChannel socketChannel = serverSocketChannel.accept();
  45. // 将socketChannel设置为非阻塞模式
  46. socketChannel.configureBlocking(false);
  47. // 将socketChannel注册到selector上,监听可读事件
  48. socketChannel.register(selector, SelectionKey.OP_READ);
  49. // 回送客户端消息
  50. socketChannel.write(StandardCharsets.UTF_8.encode("你与聊天室其他人都不是朋友关系,请注意隐私安全!"));
  51. }
  52. private void readHandler(SelectionKey selectionKey, Selector selector) throws IOException {
  53. // 从SelectionKey中获取到已经就绪的channel
  54. SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
  55. // 创建Buffer
  56. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  57. // 循环读取客户端信息
  58. StringBuilder read = new StringBuilder();
  59. while (socketChannel.read(byteBuffer) > 0) {
  60. // 切换为读模式
  61. byteBuffer.flip();
  62. // 进行读取
  63. read.append(StandardCharsets.UTF_8.decode(byteBuffer));
  64. // 清空缓冲区
  65. byteBuffer.clear();
  66. }
  67. System.out.println(read);
  68. // 将客户端发送的请求信息,广播给其他客户端
  69. // Selector.keys():获取所有注册的SelectionKey
  70. for (SelectionKey key : selector.keys()) {
  71. // 获取注册在select中的通道
  72. SelectableChannel channel = key.channel();
  73. // 如果为socketChannel
  74. if (channel instanceof SocketChannel && channel != socketChannel) {
  75. // 向其他客户端发送此客户端发送的消息
  76. ((SocketChannel) channel).write(StandardCharsets.UTF_8.encode(read.toString()));
  77. }
  78. }
  79. }
  80. public static void main(String[] args) throws IOException {
  81. NioServer nioServer = new NioServer();
  82. nioServer.start();
  83. }
  84. }

客户端

编写NIO客户端:
1、创建 SocketChannel 对象
2、设置当前客户端为非阻塞
3、连接远程服务器
4、创建 Selector 对象
5、将 SocketChannel 注册到 Selector 对象,并监听 SelectionKey.OP_READ 事件
6、轮询

  1. public class NioClient {
  2. private SocketChannel socketChannel;
  3. public NioClient(String host, Integer port) {
  4. try {
  5. // 1.创建SocketChannel
  6. socketChannel = SocketChannel.open();
  7. // 2.连接到服务端
  8. socketChannel.socket().connect(new InetSocketAddress(host, port));
  9. // 3.设置到非阻塞模式
  10. socketChannel.configureBlocking(false);
  11. } catch (IOException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. public void send(String msg) throws IOException {
  16. socketChannel.write(StandardCharsets.UTF_8.encode(msg));
  17. }
  18. public void receive() {
  19. new Thread(() -> {
  20. try {
  21. Selector selector = Selector.open();
  22. socketChannel.register(selector, SelectionKey.OP_READ);
  23. while (true) {
  24. int counts = selector.select();
  25. if (counts == 0) {
  26. continue;
  27. }
  28. Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
  29. while (iterator.hasNext()) {
  30. // 获取SelectionKey
  31. SelectionKey selectionKey = iterator.next();
  32. // 删除已经处理的SelectionKey,避免重复处理
  33. iterator.remove();
  34. // 由于只绑定了一个SocketChannel且只监听OP_READ事件
  35. if (selectionKey.isReadable()) {
  36. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  37. while (socketChannel.read(byteBuffer) > 0) {
  38. // 将byteBuffer切换为读模式
  39. byteBuffer.flip();
  40. // 控制台打印服务端消息
  41. System.out.println(StandardCharsets.UTF_8.decode(byteBuffer));
  42. // 清空byteBuffer
  43. byteBuffer.clear();
  44. }
  45. }
  46. }
  47. }
  48. } catch (IOException e) {
  49. e.printStackTrace();
  50. }
  51. }).start();
  52. }
  53. public static void main(String[] args) {
  54. NioClient nioClient = new NioClient("127.0.0.1", 10000);
  55. // 客户端接受消息
  56. nioClient.receive();
  57. Scanner scanner = new Scanner(System.in);
  58. while (true) {
  59. System.out.println("请输入要发送的信息:");
  60. String msg = scanner.nextLine();
  61. if ("close".equalsIgnoreCase(msg)) break;
  62. try {
  63. nioClient.send(msg);
  64. } catch (IOException e) {
  65. e.printStackTrace();
  66. }
  67. }
  68. }
  69. }