qq_40619811 2022-10-11 00:44 采纳率: 89.8%
浏览 80
已结题

你好,想用websocket统计在线人数,却无法统计,为什么

我的代码如下
这是WebScoket服务器

package com.example.demo8;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;
/**
 * 发送消息的类
 */
@Slf4j
@Component
@ServerEndpoint(value = "/websocket")
public class WebSocketServer {

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();


    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    //接收sid
    private String sid="";
    /**
     * 连接建立成功调用的方法*/
    @OnOpen
    public void onOpen(Session session,@PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        log.info("有新窗口开始监听:"+sid+",当前在线人数为" + getOnlineCount());
        this.sid=sid;
        try {
            sendMessage("连接成功");
        } catch (IOException e) {
            log.error("websocket IO异常");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自窗口"+sid+"的信息:"+message);
        //群发消息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.info("非正常关闭,发生错误!====>" + error.toString() + "当前在线人数为" + getOnlineCount());
    }
    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 群发自定义消息
     * */
    public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
        for (WebSocketServer item : webSocketSet) {
            try {
                //这里可以设定只推送给这个sid的,为null则全部推送
                if(sid==null) {
                    item.sendMessage(message);
                }else if(item.sid.equals(sid)){
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}

这是WebSocket支持

package com.example.demo8;

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

/**
 * 开启WebSocket支持
 */
@Configuration
public class WebSocketConfig {

    /**
     *  注入对象ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    @Bean
    public MySpringConfigurator getSpringConfigurator(){
        return new MySpringConfigurator();
    }
}

这是controller

package com.example.demo8;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpSession;
import java.io.IOException;
@org.springframework.stereotype.Controller
public class Controller {
    @RequestMapping("/myindex1")
    @ResponseBody
    @CrossOrigin
    public Model inserts(Model mp, HttpSession ses){
        try {
            WebSocketServer.sendInfo("msg","111");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return mp;
    }
}

这是ws服务器的html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
<script>
var socket;
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else{
console.log("您的浏览器支持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
var wsUrl=new WebSocket("ws://localhost:8080/myindex1")
console.log("socket 链接地址:" + wsUrl);
if (wsUrl.indexOf("https") >= 0 ) {//如果是https  webSocket 需要遵守wss协议所以这里判断如果是https socket
wsUrl = wsUrl.replace("https","wss");
}else{
wsUrl = wsUrl.replace("http","ws");
}
console.log("socket 通讯地址:" + wsUrl);
var lockReconnect = false;//避免重复连接
var ws;
var tt;
//创建链接
createWebSocket();
//创建链接
function createWebSocket() {
try {
ws = new WebSocket(wsUrl);
// 初始化链接
init();
} catch(e) {
console.log('catch'+e);
reconnect(wsUrl);
}
}

/**
* 初始化链接
*/
function init() {
ws.onclose = function () {
console.log(getNowTime() +" Socket已关闭");
reconnect(wsUrl);
};
ws.onerror = function() {
console.log(getNowTime() +' 发生异常了');
reconnect(wsUrl);
};
ws.onopen = function () {
console.log(getNowTime() +" Socket 已打开");
ws.send("连接成功");
//心跳检测重置
heartCheck.start();
};
ws.onmessage = function (event) {
console.log(getNowTime() +' 接收到消息:'+event.data);
heartCheck.start();
//拿到任何消息都说明当前连接是正常的
//实时添加消息
}
}

var lockReconnect = false;//避免重复连接
//重试连接socket
function reconnect(wsUrl) {
if(lockReconnect) {
return;
};
lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
tt && clearTimeout(tt);
tt = setTimeout(function () {
createWebSocket(wsUrl);
lockReconnect = false;
}, 180000);
}
//心跳检测
var heartCheck = {
timeout: 210000,
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
console.log(getNowTime() +" Socket 心跳检测");
var self = this;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
console.log(getNowTime() +' Socket 连接重试');
ws.send("连接成功");
self.serverTimeoutObj = setTimeout(function() {
console.log(ws);
ws.close();
}, self.timeout);
}, this.timeout)
}
}
}

/**
* 获取系统当前时间
* @returns
*/
function p(s) {
return s < 10 ? '0' + s : s;
}
function getNowTime() {
var myDate = new Date();
//获取当前年
var year = myDate.getFullYear();
//获取当前月
var month = myDate.getMonth() + 1;
//获取当前日
var date = myDate.getDate();
var h = myDate.getHours();       //获取当前小时数(0-23)
var m = myDate.getMinutes();     //获取当前分钟数(0-59)
var s = myDate.getSeconds();
return year + '-' + p(month) + "-" + p(date) + " " + p(h) + ':' + p(m) + ":" + p(s);
}
</script>
</html>


我的问题有以下几个
1、启动SpringBoot项目以后,发现websocket服务器并没有开启,没能执行onOpen、onMessage等方法,从而无法统计到在线设备数。我的要求是打开myindex1网页,就能启动WebSocket服务器,执行onOpen、onMessage等方法,从而统计到在线设备数。当我关闭浏览器时,就会执行onClose方法。
2、如果输入ws://localhost:8080/myindex1,发现浏览器会报错,错误如下

img


如果在PostMan输入ws://localhost:8080/myindex1,浏览器却报另一个错误,就是Bad Request错误,也就是400错误

img


请问这两个问题出现在哪里,怎么办?

  • 写回答

3条回答 默认 最新

  • 游一游走一走 2022-10-11 07:17
    关注

    搞错了喽,你的/myindex1是个get的http协议的服务,你的ws服务的url是/websocket哟

    img

    img


    修改上述小错误后是正常的了

    img

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(2条)

报告相同问题?

问题事件

  • 系统已结题 10月20日
  • 已采纳回答 10月12日
  • 创建了问题 10月11日

悬赏问题

  • ¥15 (标签-STM32|关键词-智能小车)
  • ¥20 关于#stm32#的问题,请各位专家解答!
  • ¥15 (标签-python)
  • ¥15 第一个已完成,求第二个做法
  • ¥20 搭建awx,试了很多版本都有错
  • ¥15 java corba的客户端该如何指定使用本地某个固定IP去连接服务端?
  • ¥15 activiti工作流问题,求解答
  • ¥15 有人写过RPA后台管理系统么?
  • ¥15 Bioage计算生物学年龄
  • ¥20 如何将FPGA Alveo U50恢复原来出厂设置哇?