具体代码已经放到github上面去了,有兴趣的朋友可以看一下:
https://github.com/chyw12798/websocket-chat


我们先实现自己向自己聊天的功能:
先创建一个SpringBoot项目,然后添加相应的依赖:

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <version>2.0.4.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>

下载完依赖后,编写一个配置类WebSocketConfig。

@Configuration
public class WebSocketConfig {

    /**
     * 再来看服务端开发, java 定义了一套 javax.servlet-api, 一个 HttpServlet 就是一个 HTTP 服务。
     * java websocket 并非基于 servlet-api 简单扩展, 而是新定义了一套 javax.websocket-api。
     * 一个 websocket 服务对应一个 Endpoint。
     * 与 ServletContext 对应, websocket-api 也定义了 WebSocketContainer, 而编程方式注册 websocket 的接口是继承自 WebSocketContainer 的 ServerContainer。
     * 一个 websocket 可以接受并管理多个连接, 因此可被视作一个 server。
     * 主流 servlet 容器都支持 websocket, 如 tomcat, jetty 等。
     * 看 ServerContainer api 文档, 可从 ServletContext attribute 找到 ServerContainer。
     **/
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

然后定义一个消息类(这里的构造器、get、setter方法可以用lombok相应注解)。

@Builder  // 建造者模式(具体看这个对象生成的样子)
public class Message {

    private String userId;
    private String message;

    public Message() {
    }

    public Message(String userId, String message) {
        this.userId = userId;
        this.message = message;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

然后再创建一个服务类:

@ServerEndpoint("/test-one")
@Component
@Slf4j
public class MyOneToOneServer {

    /**
     * 用于存放所有在线客户端
     **/
    private static Map<String, Session> clients = new ConcurrentHashMap<>(); // ConcurrentHashMap细节

    private Gson gson = new Gson();

    @OnOpen
    public void onOpen(Session session) {
        log.info("有新的客户端上线:{}", session.getId());
        clients.put(session.getId(), session);
    }

    @OnClose
    public void onClose(Session session) {
        String sessionId = session.getId();
        log.info("有客户端离线:{}", sessionId);
        clients.remove(sessionId);
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        throwable.printStackTrace();
        if (clients.get(session.getId()) != null) {
            clients.remove(session.getId());
        }
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        log.info("当前SessionId:{},收到客户端发来的消息:{}", session.getId(), message);
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

然后再 编写一个html文件内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket客户端</title>
</head>
<body>

<script type="text/javascript">
    var socket;
    if(window.WebSocket) {
        socket = new WebSocket("ws://localhost:8866/test-one"); // 与服务器建立websocket连接
        socket.onmessage = function (event) { // 客户端收到服务端发送过来的消息时就会被调用
            var ta = document.getElementById("responseText");
            ta.value = ta.value + "\n" + event.data;
        }

        socket.onopen = function (event) {
            var ta = document.getElementById("responseText");
            ta.value = "连接开启!";
        }

        socket.onclose = function (event) {
            var ta = document.getElementById("responseText");
            ta.value = ta.value + "\n" + "连接关闭!";
        }

    } else {
        alert('浏览器不支持WebSocket!')
    }

    function send(message) {
        if (!window.WebSocket) {
            return ;
        }
        if (socket.readyState == WebSocket.OPEN){
            socket.send(message);
        } else {
            alert("连接尚未开启!");
        }
    }

</script>


<form onsubmit="return false;">
    <textarea id="pushMessage" name="message" style="width: 400px; height: 200px;"></textarea>
    <input type="button" value="发送数据" onclick="send(this.form.message.value)">
    <h3>服务端输出:</h3>
    <textarea id="responseText" style="width: 400px;height: 300px;"></textarea>
    <input type="button" onclick="javascript: document.getElementById('responseText').value=''" value="清空内容">
</form>

</body>


</html>

最后的目录效果图:

SpringBoot整合netty做聊天室 springboot2+netty+websocket_spring


然后启动WebSocketApplication类,启动完毕后,右击test.html,运行页面,出来的效果:

SpringBoot整合netty做聊天室 springboot2+netty+websocket_spring_02


然后再上面的输入框里输入内容,点击发送,下面的服务端输出框就显示你输入的内容:

SpringBoot整合netty做聊天室 springboot2+netty+websocket_spring_03


下面我们实现一下一对一聊天功能。
其他代码不变,只需要修改MyOneToOneServer类的代码即可:

@ServerEndpoint("/test-one")
@Component
@Slf4j
public class MyOneToOneServer {

    /**
     * 用于存放所有在线客户端
     **/
    private static Map<String, Session> clients = new ConcurrentHashMap<>(); // ConcurrentHashMap细节

    private Gson gson = new Gson();

    @OnOpen
    public void onOpen(Session session){
        log.info("有新的客户端上线:{}",session.getId());
        clients.put(session.getId(), session);
    }

    @OnClose
    public void onClose(Session session){
        String sessionId = session.getId();
        log.info("有客户端离线:{}", sessionId);
        clients.remove(sessionId);
    }

    @OnError
    public void onError(Session session,Throwable throwable){
        throwable.printStackTrace();
        if(clients.get(session.getId()) != null){
            clients.remove(session.getId());
        }
    }

    @OnMessage
    public void onMessage(Session session,String message) {
        log.info("当前SessionId:{},收到客户端发来的消息:{}",session.getId(),message);
        this.sendTo(session.getId(),gson.fromJson(message, Message.class));
    }

    public void sendTo(String sessionId,Message message){
        Session s = clients.get(message.getUserId());
        if (s != null){
            try {
                s.getBasicRemote().sendText(sessionId+"发给你的消息:"+message.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

然后右击两次test.html,出现两个页面:

SpringBoot整合netty做聊天室 springboot2+netty+websocket_客户端_04


然后其中一个进行数据发送:

SpringBoot整合netty做聊天室 springboot2+netty+websocket_客户端_05


SpringBoot整合netty做聊天室 springboot2+netty+websocket_客户端_06


最后来个群发消息。
同样只需要改动MyOneToOneServer的代码:

@ServerEndpoint("/test-one")
@Component
@Slf4j
public class MyOneToOneServer {

    /**
     * 用于存放所有在线客户端
     **/
    private static Map<String, Session> clients = new ConcurrentHashMap<>(); // ConcurrentHashMap细节

    private Gson gson = new Gson();

    @OnOpen
    public void onOpen(Session session){
        log.info("有新的客户端上线:{}",session.getId());
        clients.put(session.getId(), session);
    }

    @OnClose
    public void onClose(Session session){
        String sessionId = session.getId();
        log.info("有客户端离线:{}", sessionId);
        clients.remove(sessionId);
    }

    @OnError
    public void onError(Session session,Throwable throwable){
        throwable.printStackTrace();
        if(clients.get(session.getId()) != null){
            clients.remove(session.getId());
        }
    }

    @OnMessage
    public void onMessage(Session session,String message) {
        log.info("当前SessionId:{},收到客户端发来的消息:{}",session.getId(),message);
        this.sendAll(session.getId(),message);
    }

    public void sendTo(String sessionId,Message message){
        Session s = clients.get(message.getUserId());
        if (s != null){
            try {
                s.getBasicRemote().sendText(sessionId+"发给你的消息:"+message.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void sendAll(String sessionId,String message){
        for(Map.Entry<String,Session> sessionEntry : clients.entrySet()){
            if (!sessionEntry.getValue().getId().equals(sessionId)){
                sessionEntry.getValue().getAsyncRemote().sendText(sessionId+"的群发消息:"+message);
            } else {
                sessionEntry.getValue().getAsyncRemote().sendText("我的群发消息:"+message);
            }
        }
    }

}

打开三个浏览器,效果图:

SpringBoot整合netty做聊天室 springboot2+netty+websocket_spring_07


参考:

https://yq.aliyun.com/articles/706878?spm=a2c4e.11155435.0.0.18a93312Q0aULD