websocket的使用

1.引入依赖

java 复制代码
<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-websocket</artifactId>

</dependency>

2.配置websocket服务

java 复制代码
@Configuration
public class WebSocketConfig {

    /**
     * 配置WebSocket服务
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

3.启动类添加注解

java 复制代码
@EnableWebSocket

4.websocket服务端逻辑

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * WebSocketServer服务端
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/{uid}")
public class WebSocketClient {
    /**
     * 静态变量,用来记录当前在线连接数,线程安全的类。
     */
    private static AtomicInteger onlineSessionClientCount = new AtomicInteger(0);
    /**
     * 存放所有在线的客户端
     */
    private static Map<String, Session> onlineSessionClientMap = new ConcurrentHashMap<>();

    /**
     * 连接uid和连接会话
     */
    private String uid;
    private Session session;


    /**
     * 连接建立成功调用的方法。由前端<code>new WebSocket</code>触发
     *
     * @param uid     每次页面建立连接时传入到服务端的id,比如用户id等。可以自定义。
     * @param session 与某个客户端的连接会话,需要通过它来给客户端发送消息
     */
    @OnOpen
    public void onOpen(@PathParam("uid") String uid, Session session) {
        /**
         * session.getId():当前session会话会自动生成一个id,从0开始累加的。
         */
        log.info("连接建立中 ==> session_id = {}, sid = {}", session.getId(), uid);
        //加入 Map中。将页面的uid和session绑定或者session.getId()与session
        //onlineSessionIdClientMap.put(session.getId(), session);
        onlineSessionClientMap.put(uid, session);
        //在线数加1
        onlineSessionClientCount.incrementAndGet();
        this.uid = uid;
        this.session = session;
        sendToOne(uid, "连接成功");
        log.info("连接建立成功,当前在线数为:{} ==> 开始监听新连接:session_id = {}, sid = {},。", onlineSessionClientCount, session.getId(), uid);
    }

    /**
     * 连接关闭调用的方法。由前端<code>socket.close()</code>触发
     *
     * @param uid
     * @param session
     */
    @OnClose
    public void onClose(@PathParam("uid") String uid, Session session) {
        //onlineSessionIdClientMap.remove(session.getId());
        // 从 Map中移除
        onlineSessionClientMap.remove(uid);

        //在线数减1
        onlineSessionClientCount.decrementAndGet();
        log.info("连接关闭成功,当前在线数为:{} ==> 关闭该连接信息:session_id = {}, sid = {},。", onlineSessionClientCount, session.getId(), uid);
    }

    /**
     * 收到客户端消息后调用的方法。由前端<code>socket.send</code>触发
     * * 当服务端执行toSession.getAsyncRemote().sendText(xxx)后,前端的socket.onmessage得到监听。
     *
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        /**
         * html界面传递来得数据格式,可以自定义.
         * {"sid":"user-1","message":"hello websocket"}
         */
        log.info("接收到消息-----------------"+message);
        List<String> uids = session.getRequestParameterMap().get("uid");
        for (String uid : uids) {
            // 接收前端拖推送改的消息
            sendToOne(uid, "来自服务端的推送=====》你好前端! 我已经成功收到消息!消息数据为:" + message);
        }
    }

    /**
     * 发生错误调用的方法
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生错误,错误信息为:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 群发消息
     *
     * @param message 消息
     */
    public void sendToAll(String message) {
        // 遍历在线map集合
        onlineSessionClientMap.forEach((onlineSid, toSession) -> {
            // 排除掉自己
            if (!uid.equalsIgnoreCase(onlineSid)) {
                log.info("服务端给客户端群发消息 ==> sid = {}, toSid = {}, message = {}", uid, onlineSid, message);
                toSession.getAsyncRemote().sendText(message);
            }
        });
    }

    /**
     * 指定发送消息
     *
     * @param toUid
     * @param message
     */
    public boolean sendToOne(String toUid, String message) {
        /*
         * 判断发送者是否在线
         */
        Session toSession = onlineSessionClientMap.get(toUid);
        if (toSession == null) {
            log.error("服务端给客户端发送消息 ==> toSid = {} 不存在, message = {}", toUid, message);
            return false;
        }
        // 异步发送
        log.info("服务端给客户端发送消息 ==> toSid = {}, message = {}", toUid, message);
        toSession.getAsyncRemote().sendText(message);
        return true;
    }

}

5.service层调用推送消息方法

java 复制代码
@Service
public class WebSocketHandler {

    @Resource
    private WebSocketClient webSocketClient;

    public boolean sendMessageOne(String userId,String content) {
        boolean flag = webSocketClient.sendToOne(userId,content);
        return flag;
    }
}

6.controller层逻辑

java 复制代码
@PostMapping("/send")
    public Map push2(@RequestBody PushDTO pushDTO){
        Map map = new HashMap();
        //1、参数校验,非空~
        if(StringUtils.isEmpty(pushDTO.getMessage()) || pushDTO.getUserId() == null || pushDTO.getPushType() == null )    {
            log.info("【消息推送】 参数异常");
            map.put("code","-1");
            map.put("msg","参数异常");
            return map;
        }

        
        //2、推送消息
        boolean flag = webSocketHandler.sendMessageOne(pushMessage.getUserId() + "", content);
        //3、如果推送成功,修改状态,返回正确的JSON
        if(flag){
            PushMessage updatePushMessage = new PushMessage();
            updatePushMessage.setId(pushMessage.getId());
            updatePushMessage.setPushState(PUSH_SUCCESS);
//            pushMessageService.updateById(updatePushMessage);
            map.put("code","200");
            map.put("msg","成功");
            return map;
        }

        //4、返回用户未上线的JSON
        map.put("code","500");
        map.put("msg","用户离线");
        return map;

    }
相关推荐
数智化管理手记4 分钟前
精益生产中的TPM管理是什么?一文破解设备零故障的密码
服务器·网络·数据库·低代码·制造·源代码管理·精益工程
末日汐2 小时前
传输层协议UDP
linux·网络·udp
RopenYuan4 小时前
FastAPI -API Router的应用
前端·网络·python
@insist1235 小时前
网络工程师-VLAN 技术原理与配置指南(软考局域网核心考点)
网络·网络工程师·软考·软件水平考试
TechWayfarer5 小时前
如何搭建企业级IP归属地查询平台?
网络·网络协议·tcp/ip
TechWayfarer5 小时前
科普:IP归属地中的IDC/机房/家庭宽带有什么区别?
服务器·网络·tcp/ip
执行部之龙6 小时前
https连接建立以及密钥加密详解
网络协议·http·https
EmbeddedCore7 小时前
守护网络通信的基石:深入解析SSL/TLS协议
网络·网络协议·ssl
(Charon)7 小时前
【网络编程】关于 KV 数据库项目搭建的一些思考:从网络层到存储层该怎么想
网络
.豆鲨包8 小时前
【计算机网络】数据链路层
网络·网络协议·计算机网络