NIO 小案例实战客户端与服务端进行通讯

13,948 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情

遇到困难时不要抱怨,既然改变不了过去,那么就努力改变未来

NIO 小案例实战客户端与服务端进行通讯

案例背景

    我们进行了NIO的相关组件学习,但是都是知道了组件的API以及作用,但是并不知道如何将组件放到一起进行使用,所以我们通过一个客户端和服务端通信的案例进行学习,并加深三大组件的使用

开发步骤

  1. 创建选择器
  2. 创建服务端 ServerSocketChannel
  3. 创建服务端的监听端口
  4. 设置服务端的 ServerSocketChannel 是非阻塞的
  5. 将服务端的 ServerSocketChannel 注册到 Selector 选择器上面,并将事件设置成连接事件
  6. 监听就绪事件
  7. 根据相应的就绪事件进行逻辑处理【连接事件、可读事件、可写事件等等】

代码实现

package com.zxp.groupChat;

import io.netty.util.CharsetUtil;

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;

/**
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2022/6/27
 * @Description 聊天记录服务端
 */
public class ChatServer {
    public static void main(String[] args) throws Exception {
        // 1. 创建选择器
        Selector selector = Selector.open();
        // 2. 创建服务端 channel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 3. 创建服务端的监听端口
        serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 6666));
        // 4.设置serversocketchannel 是非阻塞的
        serverSocketChannel.configureBlocking(false);
        // 5. 将serversocketchannel注册到selector选择器上面,并将事件设置成连接事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 监听就绪事件
        while (true) {
            System.out.println("等待......");
            // 休眠1秒  无论是否有读写事件发生 selector每隔1秒被唤醒
            int selected = selector.select(1000);
            if (selected > 0) { // 证明有事件已经准备就绪
                // 返回已经就绪的事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                if (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    // 获取socketChannel
                    if (key.isAcceptable()) { // 连接事件就绪,将其感兴趣的事件设置成已读事件
                        // 处理接入的新请求
                        handleAccept(selector, key);
                    }
                    if (key.isReadable()) { // 已读事件就绪
                        // 处理通道的读请求
                        handleRead(key);
                    }
                    iterator.remove();// 将处理完的数据进行了移除
                }
            }
        }

    }

    /**
     * 处理客户端读操作请求
     * @param key
     */
    private static void handleRead(SelectionKey key) {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        // 申请一个buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 将通道的数据读入到buffer中
        try {
            socketChannel.read(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("客户端发来消息: " + new String(buffer.array(), 
                                                    CharsetUtil.UTF_8));
    }

    /**
     * 处理连接操作
     * @param selector
     * @param key
     */
    private static void handleAccept(Selector selector, SelectionKey key) {
        // 通过 ServerSocketChannel 监听过来连接的客户端事件
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        //通过调用 accept 方法,返回一个具体的客户端连接管道
        try {
            SocketChannel socketChannel = serverChannel.accept();
            System.out.println("客户端 " + 
                               socketChannel.getRemoteAddress() + "已上线......");
            // 将channel 注册到selector 上面,而且需要设置成是非阻塞的
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_READ);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

结果展示

    由于我们是在windows平台下进行的测试所以我们就用了命令行工具 cmd ,通过Telnet工具进行的测试操作,但是我们进行指令操作的时候有可能提示这个工具不存在,那么我们就需要进行将这个工具开放出来【windows 默认是关闭的】

开启Telnet工具

1. 打开控制面板

image.png

2. 打开程序

image.png

3. 启用或关闭 Windows 功能

image.png
    在图中将其√选中,之后确定保存就可以了

连接服务端

    连接服务端指令:telnet IP地址 端口号

telnet 127.0.0.1 6666

image.png
    没连接之前隔1s会一直打印 等待......
image.png
    连接成功后服务端会有客户端提示已上线的操作显示
image.png
    此时客户端会一直是黑色的,你需要按下Ctrl+] 这个键子,之后通过send指令进行消息发送
image.png
    此时在cmd控制台通过telnet 的send指令向服务端发送hello操作
image.png
    发送结果
image.png
    此时客户端已经和服务端进行了连接成功并进行了消息发送操作

总结

    到此我们已经将NIO的三大组件channel、buffer、selector 进行了结合实现了服务端与客户端的连接操作,并且通过客户端也给服务端进行了消息发送,服务端进行了消息打印处理,后续我们还会做一个群聊的通讯案例进行NIO 基础知识的学习