websocket

websocket介绍:

websocket协议是基于Tcp的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信–允许服务器主动发送信息给客户端。
websocket使用场景:

  • 弹幕
  • 网页聊天系统
  • 实时监控
  • 股票行情推送

socketjs介绍

socketjs是什么:

  • 是一个浏览器JavaScript库,提供了一个类似WebSocket的对象。
  • 提供了一个连贯的跨浏览器的javaScriptAPI,在浏览器和web服务器之间创建了一个低延迟、全双工,跨域的通信通道。
  • 在底层SockJS首先尝试使用本地WebSocket。如果失败了,它可以使用各种浏览器特定的传输协议,并通过类似WebSocket的抽象方式呈现他们。
  • SockJs旨在适用于所有线代浏览器和不支持WebSocket协议的环境。

学习资料:socketjs

stompjs

stompjs是什么:

  • STOMP Simple (or Streaming) Text Orientated Messaging Protocol 它定义了可互操作的连线格式,以便任何可用的STMOP客户端都可以与任何STOMP消息代理进行通信,以在语言和平台之间提供简单而广的消息互操作性(归纳一句话:是一个简单的面向文本的消息传递协议。)

学习资料:stompjs

广播、单播、多播

  • 单播(Unicast):点对点,私信私聊
  • 广播(Broadcast)(所有人):游戏公告,发布订阅
  • 多播,组播(Multicast)(特地人群) :多人聊天,发布订阅

广播技术应用

简单websocket游戏公告系统《一》主要内容:springboot框架搭建和maven依赖资料地址:spring提供的WebSocket教学

webjars使用基本介绍特点

  • 1、方便统一管理
  • 2主要解决前端框架版本不一致,文件混乱等问题3把前端资源,打包成iar包,借助maven工具进行管理

资料:webjars

游戏公告系统

使用Springboot构建基于STOMP的WebSocket广播式通信
Spring中的WebSocket架构
架构图:
在这里插入图片描述
图中各个组件介绍:
生产者型客户端(左上组件): 发送SEND命令到某个目的地址(destination)的客户端。
消费者型客户端(左下组件): 订阅某个目的地址(destination), 并接收此目的地址所推送过来的消息的客户端。
request channel: 一组用来接收生产者型客户端所推送过来的消息的线程池。
response channel: 一组用来推送消息给消费者型客户端的线程池。
broker: 消息队列管理者,也可以成为消息代理。它有自己的地址(例如“/topic”),客户端可以向其发送订阅指令,它会记录哪些订阅了这个目的地址(destination)。
应用目的地址(图中的”/app”): 发送到这类目的地址的消息在到达broker之前,会先路由到由应用写的某个方法。相当于对进入broker的消息进行一次拦截,目的是针对消息做一些业务处理。
非应用目的地址(图中的”/topic”,也是消息代理地址): 发送到这类目的地址的消息会直接转到broker。不会被应用拦截。
SimpAnnotatonMethod: 发送到应用目的地址的消息在到达broker之前, 先路由到的方法. 这部分代码是由应用控制的。
在这里插入图片描述
我们先建立服务器
首先是配置类代码:

package net.xdclass.websocketstudy.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    注册端点,发布或者订阅消息的时候需要连接此端点
    //withSockJS表示开始socketjs支持
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/endpoint-websocket").setAllowedOriginPatterns("*").withSockJS();
    }
    ///配置消息代理中介
    //enableSimpleBroker 服务端推送给客户端的路径前缀
    //setApplicationDestinationPrefixes 客户端推送给服务端的路径前缀
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
       registry.enableSimpleBroker("/topic","/chat");///启动SimpleBroker,使得订阅到此"topic"前缀的客户端可以收到公告
       registry.setApplicationDestinationPrefixes("/app");  在客户端推送给服务端也就是/app/v1/chat,这样就会被gameInfo方法来处理
    }
}

enableSimpleBroker就类似于
在这里插入图片描述

ApplicationDestinationPrefixes就类似于simpAnnotationMethod
在这里插入图片描述
定义两个类,来分别表示客户端传入服务端的消息和服务端传入客户端的消息

package net.xdclass.websocketstudy.model;

import java.util.Date;

public class InMessage {
    private String from;
    private String to;
    private String content;
    private Date data;

    public InMessage() {
    }

    public InMessage(String from, String to, String content, Date data) {
        this.from = from;
        this.to = to;
        this.content = content;
        this.data = data;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Date getData() {
        return data;
    }

    public void setData(Date data) {
        this.data = data;
    }
}

```java
package net.xdclass.websocketstudy.model;

import java.util.Date;

public class OutMessage {
    private String from;
    private String content;
    private Date time=new Date();

    public OutMessage() {
    }

    public OutMessage(String content) {
        this.content = content;
    }

    public OutMessage(String from, String content, Date time) {
        this.from = from;
        this.content = content;
        this.time = time;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Date getTime() {
        return time;
    }

    public void setTime(Date time) {
        this.time = time;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }
}


接着就是controller方法

```java
package net.xdclass.websocketstudy.controller.v1;

import net.xdclass.websocketstudy.model.InMessage;
import net.xdclass.websocketstudy.model.OutMessage;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import org.springframework.messaging.handler.annotation.MessageMapping;

import javax.websocket.OnMessage;

@Controller
public class GameInfoController {
    //MessageMapping也是一个路由,他和RequestMapping也是一样的路径映射,只不过它是针对websocket去使用的。
    @MessageMapping("v1/chat")
    /// SendTo就是发送到SimpleBroker,然后再由SimpleBroker转发到订阅到这个路径的客户。
    @SendTo("/topic/game_chat")
    public OutMessage gameInfo(InMessage message)
    {
        return new OutMessage(message.getContent());
    }
}

然后我们看js代码

var stompClient = null;
/*
这个函数就是防止重复连接
 */
function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
    }
    else {
        $("#conversation").hide();
    }
    $("#notice").html("");
}

function connect() {
    var socket = new SockJS('/endpoint-websocket'); //连接上端点(基站)
    
    stompClient = Stomp.over(socket);			//用stom进行包装,规范协议
    stompClient.connect({}, function (frame) {	
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/game_chat', function (result) {让这个客户端订阅到/topic/game_chat目的,然后只要有消息发送到该地址
        	console.info(result)///这个客户端就会接收到消息放入result中。
        	showContent(JSON.parse(result.body));
        });
    });
}

function disconnect() {
    if (stompClient !== null) {
        stompClient.disconnect();
    }
    setConnected(false);
    console.log("Disconnected");
}

function sendName() {这个函数是管理员发送公告,也就是客户端向服务端发送东西所以是以/app开头的,然后经过服务端处理,最后又会发送到
	/topic/game_chat中然后订阅该地址的客户端就能看到消息啦。
    stompClient.send("/app/v1/chat", {}, JSON.stringify({'content': $("#content").val()}));
}

function showContent(body) {
    $("#notice").append("<tr><td>" + body.content + "</td> <td>"+new Date(body.time).toLocaleString()+"</td></tr>");
}





$(function () {
    $("form").on('submit', function (e) {
        e.preventDefault();
    });
    $( "#connect" ).click(function() { connect(); });
    $( "#disconnect" ).click(function() { disconnect(); });
    $( "#send" ).click(function() { sendName(); });
});

websocket推送两种推送方法的区别和讲解

简介:
	讲解websocket推送方式:@SendTo注解和SimpMessagingTemplate的区别

笔记:	
	1、SendTo 不通用,固定发送给指定的订阅者
	2、SimpMessagingTemplate 灵活,支持多种发送方式
	我们可以将
	SimpMessagingTemplate放在一个service包中。
package net.xdclass.websocketstudy.service;

import net.xdclass.websocketstudy.model.InMessage;
import net.xdclass.websocketstudy.model.OutMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

/*
简单消息模板,同来推送消息
 */
@Service
public class WebSocketService {

    @Autowired
    private SimpMessagingTemplate template;
    public void sendTopicMessage(String dest,InMessage message)
    {
        for(int i=0;i<20;i++)
        template.convertAndSend(dest,new OutMessage((message.getContent())+i));
    }

}

然后再去调用

package net.xdclass.websocketstudy.controller.v2;

import net.xdclass.websocketstudy.model.InMessage;
import net.xdclass.websocketstudy.service.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;

@Controller
public class V2GameInfoController {
    @Autowired
    private WebSocketService ws;
    //MessageMapping也是一个路由,他和RequestMapping也是一样的路径映射,只不过它是针对websocket去使用的。
    @MessageMapping("v2/chat")
    /// SendTo就是发送到SimpleBroker,然后再由SimpleBroker转发到订阅到这个路径的客户。
    //@SendTo("/topic/game_chat")
    public void gameInfo(InMessage message){
        ws.sendTopicMessage("/topic/game_rank",message);
    }
}

Springboot针对websocket 4类的监听器介绍和使用

简介:
	SpringBoot里面websocekt监听器的使用,包含订阅、取消订阅,socekt连接和断开连接4类监听器的编写和使用

笔记:
	注意点:
		1、需要监听器类需要实现接口ApplicationListener<T> T表示事件类型,下列几种都是对应的websocket事件类型
		2、在监听器类上注解 @Component,spring会把改类纳入管理

	websocket模块监听器类型:
		SessionSubscribeEvent 	订阅事件
		SessionUnsubscribeEvent	取消订阅事件
		SessionDisconnectEvent 	断开连接事件
		SessionDisconnectEvent 	建立连接事件

例如:

package net.xdclass.websocketstudy.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectEvent;
import org.springframework.web.socket.messaging.SessionSubscribeEvent;

@Component
public class ConnectionEventListener implements ApplicationListener<SessionConnectEvent> {

    @Override
    public void onApplicationEvent(SessionConnectEvent event) {
        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
        System.out.println("[ConnectionEventListener监听器事件 类型]"+headerAccessor.getCommand().getMessageType());
    }
}
package net.xdclass.websocketstudy.listener;

import org.springframework.context.ApplicationListener;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionSubscribeEvent;
@Component
public class SubscribeEventListener implements ApplicationListener<SessionSubscribeEvent> {
    @Override
    public void onApplicationEvent(SessionSubscribeEvent event) {

        StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
        System.out.println("[监听器事件 类型]"+headerAccessor.getCommand().getMessageType());
    }
}

点对点聊天和简单消息头介绍

简介:
	使用socketjs订阅API,进行点对点聊天;StompHeaderAccessor简单介绍
StompHeaderAccessor简单消息传递协议中处理消息头的基类,
通过这个类,可以获取消息类型(例如:发布订阅,建立连接断开连接),会话id等。
@Controller
public class V3GameInfoController {
    @Autowired
    private WebSocketService ws;
    //MessageMapping也是一个路由,他和RequestMapping也是一样的路径映射,只不过它是针对websocket去使用的。
    @MessageMapping("v3/single/chat")
    /// SendTo就是发送到SimpleBroker,然后再由SimpleBroker转发到订阅到这个路径的客户。
    //@SendTo("/topic/game_chat")
    public void singleChat(InMessage message){
        ws.sendChatMessage(message);
    }
}
 public void sendChatMessage(InMessage message)
    {
        template.convertAndSend("/chat/single/"+message.getTo(),new OutMessage(message.getFrom()+" 发送:"+message.getContent()));
    }

这样就能实现点对点聊天。

websocket结合springboot定时推送,实时监控JVM系统负载

简介:
	websocket结合springboot的注解Scheduled实现定时推送,使用服务端定时推送注意事项;
	开发简单监控JVM监控功能

笔记:
	1、在controller的类方法上标注 @Scheduled(fixedRate = 3000) 表示这个方法会定时执行
	fixedRate表示是多少毫秒 3000就3秒

	2、需要在springboot启动类上@EnableScheduling

	3、被注解@Scheduled标记的方法,是不能有参数,不然会报错

类似的我们可以实现监控java虚拟机内存大小:

@Controller
public class V4ServerInfoController {
    @Autowired
    private WebSocketService ws;
    @MessageMapping("/v4/schedule/push")
    @Scheduled(fixedRate=3000)三秒调用///加了这个注解的方法不能带任何参数否则会报错
    public void sendServerInfo()
    {
        ws.sendServerInfo();
    }

}
 public void sendServerInfo()
    {
        int processors=Runtime.getRuntime().availableProcessors();
        long freeMem = Runtime.getRuntime().freeMemory();
        long maxMem = Runtime.getRuntime().maxMemory();
        String message=String.format("服务器可用处理器: %s;虚拟机空闲内存大小: %s; 最大内存大小: %s",processors,freeMem,maxMem);
        template.convertAndSend("/topic/server_info",new OutMessage(message));
    }

项目应用之股票行情推送实战《一》 效果展示,阿里云API介绍和httpClient工具类使用

简介:
	展示简单股票行情推送的效果,及介绍阿里云API市场,httpClient工具类的使用

笔记:
	访问地址:localhost:8080/v5/index.html
	阿里云API市场: https://market.aliyun.com/data?spm=5176.8142029.388261.183.346bc16fAs3slP
	HttpClientUtils下载: https://github.com/aliyun/api-gateway-demo-sign-java

这样我们就可以利用阿里云API的数据然后通过服务器发送到客户端。

websocket结合spring相关拦截器使用 拦截器介绍,及HandshakeInterceptor握手拦截器介绍

简介:
	拦截器介绍,springBoot结合websocket相关拦截器使用,握手拦截器的开发和使用

笔记:
	1、编写一个类,实现一个接口HandshakeInterceptor;写完之后需要在websocket配置里面启用
	.addInterceptors(new HttpHandShakeIntecepter())
	
	2、实现两个方法beforeHandshake和afterHandshake,在里面可以获取resuest和response
package net.xdclass.websocketstudy.intecepter;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import javax.servlet.http.HttpSession;
import java.util.Map;
/*
http握手拦截器,可以通过这个类的方法获取request,和response
 */
@Component
public class HttpHandShakeIntecepter implements HandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        System.out.println("握手拦截器 beforeHandshake");
        if(request instanceof ServletServerHttpRequest)
        {
            ServletServerHttpRequest servletRequest= (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession();
            String id = session.getId();
            System.out.println("握手拦截器 beforeHandshake sessionId="+id);
            attributes.put("sessionId",id);在这里放入之后到后面的listener也可以拿到
        }
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
        System.out.println("握手拦截器 afterHandshake");
    }
}

我们可以拿到HttpServletRequest,在这里面我们可以干很多事情,
在这里插入图片描述之后到监视器还能拿到我们在拦截器里面设置的数据。

        System.out.println("[ConnectionEventListener监听器事件 类型]"+headerAccessor.getSessionAttributes().get("sessionId"));

16、用户在线状态维护功能介绍和登录API开发

简介:
	用户状态功能相关接口开发和登录API接口开发
package net.xdclass.websocketstudy.controller.v6;

import net.xdclass.websocketstudy.service.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

@Controller
public class UserChatController {
    @Autowired
    private WebSocketService ws;
    ///模拟数据库用户的数据
    public static Map<String,String>userMap=new HashMap<>();
    static{
        userMap.put("jack","123");
        userMap.put("mary","456");
        userMap.put("tom","789");
        userMap.put("tim","000");
        userMap.put("小D","666");
    }
    public static Map<String,User>onlineUser=new HashMap<>();
    static{
        onlineUser.put("123",new User("admin","888"));
    }
    @RequestMapping(value = "login",method= RequestMethod.POST)
    public String userlogin(@RequestParam(value="username",required = true) String username, @RequestParam(value = "pwd",
    required = true) String pwd, HttpSession session)
    {
        String password = userMap.get(username);
        if(pwd!=null&&pwd.equals(password))
        {
            User user=new User(username,pwd);
            String sessionId=session.getId();
            onlineUser.put(sessionId,user);
            return "redirect:/v6/chat.html";
        }
        return "redirect:/v6/error.html";
    }
}

spring channel拦截器介绍及用户上线下线功能开发

简介:
	channel频道拦截器使用讲解,结合StompHeaderAccessor实现用户上线下线功能
笔记:
	1、ChannelInterceptorAdapter 频道拦截器适配器,具体实现的接口是ChannelIntecepter
	
	2、需要ChannelInterceptorAdapter子类重写override对应的方法,实现自己的逻辑,主要是
	public void postSend(Message<?> message, MessageChannel channel, boolean sent) 

	3、ChannelInterceptorAdapter子类需要在配置Websocket的配置里面加入

	4、在配置类里面加入
		@Override
		public void configureClientInboundChannel(ChannelRegistration registration) {
			registration.interceptors( new SocketChannelIntecepter());
		}

		@Override
		public void configureClientOutboundChannel(ChannelRegistration registration) {
			registration.interceptors( new SocketChannelIntecepter());
		}


资料:
	https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/messaging/support/ChannelInterceptorAdapter.html
握手拦截器 beforeHandshake
握手拦截器 beforeHandshake sessionId=17DD2BFBD372D1461B0FC27E0A030D26
握手拦截器 afterHandshake
SocketChannelIntecepter->preSend
SocketChannelIntecepter->postSend
SocketChannelIntecepter ->sessionId=17DD2BFBD372D1461B0FC27E0A030D26
connect sessionId=17DD2BFBD372D1461B0FC27E0A030D26
SocketChannelIntecepter->afterSendCompletion
[ConnectionEventListener监听器事件 类型]CONNECT
[ConnectionEventListener监听器事件 类型]17DD2BFBD372D1461B0FC27E0A030D26
SocketChannelIntecepter->preSend
SocketChannelIntecepter->postSend
SocketChannelIntecepter->afterSendCompletion
SocketChannelIntecepter->preSend
SocketChannelIntecepter->postSend
SocketChannelIntecepter ->sessionId=17DD2BFBD372D1461B0FC27E0A030D26
SocketChannelIntecepter->afterSendCompletion
[监听器事件 类型]SUBSCRIBE

由此可见
SocketChannelIntecepter是一个从外到里再从里到外的过程,使用这个拦截器,我咋感觉更像监听器,只要连接或者不连接订阅或者不订阅的时候我们都能监听到,这样我们就能做出用户上线或者下线的操作,只要用户下线,意思就是断开连接,然后我们可以通过StompHeaderAccessor获取到session,然后通过getCommand方法,获取状态,判断状态,如果断开连接,我们就把用户数据根据sessionId移除掉就好了。

package net.xdclass.websocketstudy.intecepter;

import net.xdclass.websocketstudy.controller.v6.UserChatController;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.ChannelInterceptorAdapter;

/*
频道拦截器,类似管道,可以获取消息的一些meta数据
 */
public class SocketChannelIntecepter extends ChannelInterceptorAdapter {

    /*
    在消息实际发送到频道之前调用
     */
    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        System.out.println("SocketChannelIntecepter->preSend");
        return super.preSend(message, channel);
    }
    /*
    发送消息调用后立即调用
     */
    @Override
    public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
        System.out.println("SocketChannelIntecepter->postSend");
        StompHeaderAccessor headerAccessor=StompHeaderAccessor.wrap(message);
        if(headerAccessor.getCommand()==null)return ;
        String sessionId = headerAccessor.getSessionAttributes().get("sessionId").toString();
        System.out.println("SocketChannelIntecepter ->sessionId="+sessionId);
        switch (headerAccessor.getCommand())
        {
            case CONNECT:
                connect(sessionId);
                break;
            case DISCONNECT:
                disconnect(sessionId);
                break;
            case SUBSCRIBE:
                break;
            case UNSUBSCRIBE:
                break;
        }

    }
    void connect(String sessionId)
    {
        System.out.println("connect sessionId="+sessionId);
    }
    void disconnect(String sessionId)
    {
        System.out.println("disconnect sessionId="+sessionId);
        ///用户下线操作

        UserChatController.onlineUser.remove(sessionId);
    }


    /*
    在发送完成之后完成调用,不管是否有异常发生,一般用于资源释放
     */
    @Override
    public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
        System.out.println("SocketChannelIntecepter->afterSendCompletion");
        super.afterSendCompletion(message, channel, sent, ex);
    }
}

18.实时推送在线用户功能开发

简介:使用schdule注解,推送在线用户接口开发
 @Scheduled(fixedRate = 2000)
    public void onlinUser()
    {
        ws.sendOnlineUser(onlineUser);
    }

实时推送我们的onlineUser。

public void sendOnlineUser(Map<String, User> onlineUser) {
        String msg="";
        for(Map.Entry<String,User>entry:onlineUser.entrySet())
        {
            msg=msg.concat(entry.getValue().getUsername()+" || ");
        }
        template.convertAndSend("/topic/onlineuser",new OutMessage(msg));
    }

然后是多人在线聊天。

 @MessageMapping("v6/chat")
    //SimpMessageHeaderAccessor headerAccessor
    public void topicChat(InMessage message, SimpMessageHeaderAccessor headerAccessor)
    {
        String sessionId = headerAccessor.getSessionAttributes().get("sessionId").toString();
        User user=onlineUser.get(sessionId);
        message.setFrom(user.getUsername());
        ws.sendTopicChat(message);
    }

多人聊天很简单,只要通过sessionId找到发出信息的用户,然后展示在客户端上就行了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值