springboot 分布式聊天室_springboot 分布式聊天室


牛逼的地方在于,这是个github和twitter能实现同时聊天的场景,可能根据cookie自动获取了我的twitter并且登陆了。所以我的ID后面是有twitter的(最后一个),其他人没有。

为你自己的博客做个新浪QQ微信的快捷登陆并不难,但是国外的这个emmm,第一次见。后来了解到作者的确是大佬,打开自己的资料就是twitter的信息。

那么接下来用springboot写个简陋的聊天室?

Webscoket

WebSocket 是 HTML5 新增的一种在单个 TCP 连接上进行全双工通讯的协议,与 HTTP 协议没有太大关系….

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

当你获取 WebSocket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage() 事件来接收服务器返回的数据…

长连接

与 AJAX 轮训的方式差不多,但长连接不像 AJAX 轮训一样,而是采用的阻塞模型(一直打电话,没收到就不挂电话);客户端发起连接后,如果没消息,就一直不返回 Response 给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
接下来:
利用 Spring Boot 与 WebSocke 打造 一对一 和 一对多 的在线聊天….

首先是dependency!

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
</dependencies>

springboot-starter-websocket就是主角了。。。
socket这个es基础有讲呢!
utils类。。。

package com.battcn.utils;

import javax.websocket.RemoteEndpoint;
import javax.websocket.Session;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author liqiqiorz
 * @since 2020/4/16 
 */
public final class WebSocketUtils {

    /**
     * 模拟存储 websocket session 使用
     */
    public static final Map<String, Session> LIVING_SESSIONS_CACHE = new ConcurrentHashMap<>();

    public static void sendMessageAll(String message) {
        LIVING_SESSIONS_CACHE.forEach((sessionId, session) -> sendMessage(session, message));
    }

    /**
     * 发送给指定用户消息
     *
     * @param session 用户 session
     * @param message 发送内容
     */
    public static void sendMessage(Session session, String message) {
        if (session == null) {
            return;
        }
        final RemoteEndpoint.Basic basic = session.getBasicRemote();
        if (basic == null) {
            return;
        }
        try {
            basic.sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端点
@ServerEndpoint 中的内容就是 WebSocket 协议的地址,其实仔细看会发现与 @RequestMapping 也是异曲同工的…

HTTP 协议:http://localhost:8080/path
WebSocket 协议:ws://localhost:8080/path

@OnOpen、@OnMessage、@OnClose、@OnError 注解与 WebSocket 中监听事件是相对应的…

package com.battcn.websocket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

import static com.battcn.utils.WebSocketUtils.LIVING_SESSIONS_CACHE;
import static com.battcn.utils.WebSocketUtils.sendMessage;
import static com.battcn.utils.WebSocketUtils.sendMessageAll;

/**
 * 聊天室
 *
 * @author liqiqiorz
 * @since 2020/04/16 
 */
@RestController
@ServerEndpoint("/chat-room/{username}")
public class ChatRoomServerEndpoint {

    private static final Logger log = LoggerFactory.getLogger(ChatRoomServerEndpoint.class);

    @OnOpen
    public void openSession(@PathParam("username") String username, Session session) {
        LIVING_SESSIONS_CACHE.put(username, session);
        String message = "欢迎用户[" + username + "] 来到聊天室!";
        log.info(message);
        sendMessageAll(message);

    }

    @OnMessage
    public void onMessage(@PathParam("username") String username, String message) {
        log.info(message);
        sendMessageAll("用户[" + username + "] : " + message);
    }

    @OnClose
    public void onClose(@PathParam("username") String username, Session session) {
        //当前的Session 移除
        LIVING_SESSIONS_CACHE.remove(username);
        //并且通知其他人当前用户已经离开聊天室了
        sendMessageAll("用户[" + username + "] 已经离开聊天室了!");
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        throwable.printStackTrace();
    }


    @GetMapping("/chat-room/{sender}/to/{receive}")
    public void onMessage(@PathVariable("sender") String sender, @PathVariable("receive") String receive, String message) {
        sendMessage(LIVING_SESSIONS_CACHE.get(receive), "[" + sender + "]" + "-> [" + receive + "] : " + message);
    }

}

贫穷的HTML界面当然可以用bootstrap改一改,蒟蒻这里不写了。。
网上不少模板的
onopen 建立 WebSocket 连接时触发。
message 客户端监听服务端事件,当服务端向客户端推送消息时会被监听到。
error WebSocket 发生错误时触发。
close 关闭 WebSocket 连接时触发。

up:截图被吞了,我们跳过html吧
有时间传到github得了
假装我是html
主函数

package com.battcn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;


/**
 * @author liqiqiorz
 */
@EnableWebSocket
@SpringBootApplication
public class Chapter24Application {

    public static void main(String[] args) {
        SpringApplication.run(Chapter24Application.class, args);

    }

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

测试

启动 Chapter24Application.java 中的 main 方法,为了更好的演示效果这里打开了俩浏览器窗口做的测试…

springboot 分布式聊天室_客户端_02


总之实现图一的那种量级的肯定很困难。。。自娱自乐一下