正在加载中,请稍后
WebSocket 入门 Java Springboot + Html5 JavaScript 简单实现
2022-01-17 阅读 0 评论 0



前言

入门 WebSocket
后端 Springboot
前端 HTML5 (原生方法)

简单介绍下WebSocket,是类似于HTTP的基于TCP连接协议,相比于HTTP是单次单向传输的短连接,WebSocket是多次双向传输的长连接,更适合用于游戏、聊天室等有实时监听的需求

本来是设想直接用H5,其中的一个房主作为主机,就可以省去服务端,但考虑到可能存在公网ip变动、端口不开放、防火墙等问题,决定放弃该方案

所以本文还是使用主流方案实现前后端websocket交互,即所有用户与服务器端建立连接,服务器端代理实现用户端所有请求和响应



折腾

Java SpringBoot端
新建一个空的SpringBoot项目
我这里选择 JDK8 + maven + war包 (可以根据需要自行选择)
(过程省略)



修改访问端口
application.yml

server:
  port: 3000
YAML



增加maven依赖
(这里如果只提供socket接口,可以把不需要的去掉,例如web依赖可以不要,只保留websocket)
pom.xml

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



新建 websocket配置
WebSocketConfig.java

package com.zzzmh.ws.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;


/**
 * @author zzzmh
 * @date 2021/10/11
 */
@Configuration
public class WebSocketConfig {

    /**
     * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
Java



新建 websocket接口
TestSocket.java

package com.zzzmh.ws.socket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestParam;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * @author zzzmh
 * @date 2021/10/11
 */
@Component
@ServerEndpoint(value = "/test/{username}")
public class TestSocket {

    private Logger log = LoggerFactory.getLogger(getClass());

    /**
     * 记录当前在线连接数 (线程安全)
     */
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    /**
     * 存放所有在线的客户端 (线程安全)
     */
    private static Map<String, Session> clients = new ConcurrentHashMap<>();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        clients.put(session.getId(), session);
        // 在线人数加1
        onlineCount.incrementAndGet();
        log.info("有新连接加入:{},当前在线人数为:{}", session.getId(), onlineCount.get());

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        onlineCount.decrementAndGet(); // 在线数减1
        clients.remove(session.getId());
        log.info("有一连接关闭:{},当前在线人数为:{}", session.getId(), onlineCount.get());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("服务端收到客户端[{}]的消息:{}", session.getId(), message);
        this.sendMessage(message, session);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 群发消息
     *
     * @param message 消息内容
     */
    private void sendMessage(String message, Session fromSession) {
        for (Map.Entry<String, Session> sessionEntry : clients.entrySet()) {
            Session toSession = sessionEntry.getValue();
            String username = toSession.getPathParameters().get("username");
            // 排除掉自己
            if (!fromSession.getId().equals(toSession.getId())) {
                String fromUsername = fromSession.getPathParameters().get("username");
                log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
                toSession.getAsyncRemote().sendText(message + "( 发送者: " + fromUsername + ", 接受者: " + username + ")");
            }
        }
    }
}
Java

前端html代码 (需要放在容器里才能执行,本地执行无法请求接口,我这里用webstorm编辑和执行,也可以用vscode)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>websocket</title>
</head>
<body>

<input id="text" type="text"/>
<button onclick="send()">Send</button>
<button onclick="closeWebSocket()">Close</button>
<div id="message"></div>


<script type="text/javascript">
    if (!'WebSocket' in window) {
        alert('当前浏览器不支持WebSocket 无法继续进行游戏,请更换浏览器或设备!')
    }

    let username;
    while (!username) {
        username = prompt("请输入你的游戏昵称:");
    }

    const websocket = new WebSocket("ws://localhost:3000/test/" + username);

    //连接发生错误的回调方法
    websocket.onerror = function () {
        setMessageInnerHTML("连接错误");
    };

    //连接成功建立的回调方法
    websocket.onopen = function (event) {
        setMessageInnerHTML("已连接");
    }

    //接收到消息的回调方法
    websocket.onmessage = function (event) {
        setMessageInnerHTML("已收到消息:" + event.data);
    }

    //连接关闭的回调方法
    websocket.onclose = function () {
        setMessageInnerHTML("已断开");
    }

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function () {
        websocket.close();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += '<br>' + innerHTML;
    }

    //关闭连接
    function closeWebSocket() {
        websocket.close();
    }

    //发送消息
    function send() {
        const message = document.getElementById('text').value;
        setMessageInnerHTML("已发送消息:" + message)
        websocket.send(message);
    }
</script>
</body>
</html>
HTML



服务端大致目录如下

ws
├── src
│   ├── main
│   │   ├── java
│   │   │   ├── com.zzzmh.ws
│   │   │   │   ├── config
│   │   │   │   │   ├── WebSocketConfig.java
│   │   │   │   ├── socket
│   │   │   │   │   ├── TestSocket.java
│   │   │   │   ├── ServletInitializer.java
│   │   │   │   ├── WsApplication.java
│   │   ├── resources
│   │   │   ├── application.yml
├── pom.xml
Shell



效果展示

首先我这里打开3个页面模拟3个用户,分别输入昵称 user1 user2 user3
分别都可以看到已连接 说明连接websocket正常
在user1 中输入666并点击发送
在user2 和 user3可以看到效果
截图如下

user1


user2


user3



END

后续打算写几个能联机的多人游戏玩一玩
当其中一个人进行操作,就可以用ws实时同步到其他玩家的界面
包括实时的多人在线聊天也可以用这个实现
用户本地缓存数据用 localStorage
还可以实现断线重连


最终代码
https://gitee.com/tczmh/ws-java
https://gitee.com/tczmh/websocket-html


参考
https://www.cnblogs.com/xuwenjin/p/12664650.html

评论区空空如也,赶紧添加一条评论吧
zzzmh
Hello 你好鸭 O(∩_∩|
关于我

网址导航