Spring整合WebSocket(三)下

简介: Spring整合WebSocket(三)下

二.三 实体封装处理和控制器


二.三.一 封装用户信息 User

里面存入 id (uuid自动生成,避免重复) 和 nickName (用户输入昵称) 。 可以用数据库替换这个用户信息

package com.yjl.websocket.pojo;
/**
 * 封装用户信息对象
 * @author 两个蝴蝶飞
 *
 */
public class User {
  /**
  * @param id 编号,是uuid
  * @param nickName 昵称,由用户自己输入
  */
  private String id;
  private String nickName;
  public String getId() {
  return id;
  }
  public void setId(String id) {
  this.id = id;
  }
  public String getNickName() {
  return nickName;
  }
  public void setNickName(String nickName) {
  this.nickName = nickName;
  }
  @Override
  public String toString() {
  return "User [id=" + id + ", nickName=" + nickName + "]";
  }
}


二.三.二 封装消息内容 MyMessage

里面有 发送者,接收者,发送内容,发送时间等重要的信息

package com.yjl.websocket.bean;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
 * 封装消息对象
 * @author 两个蝴蝶飞
 *
 */
public class MyMessage {
  /**
  * @param fromId 发送者
  * @param fromNickName 发送者的昵称
  * @param toId 接收者,如果是群发的话,为空
  * @param text 发送的内容
  * @param date 发送的时间,具体到秒
  */
  private String fromId;
  private String fromNickName;
  private String toId;
  private String text;
  //格式化成 yyyy-MM-dd HH:mm:ss的格式
  @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
  private Date date;
  public String getFromId() {
  return fromId;
  }
  public void setFromId(String fromId) {
  this.fromId = fromId;
  }
  public String getFromNickName() {
  return fromNickName;
  }
  public void setFromNickName(String fromNickName) {
  this.fromNickName = fromNickName;
  }
  public String getToId() {
  return toId;
  }
  public void setToId(String toId) {
  this.toId = toId;
  }
  public String getText() {
  return text;
  }
  public void setText(String text) {
  this.text = text;
  }
  public Date getDate() {
  return date;
  }
  public void setDate(Date date) {
  this.date = date;
  }
  @Override
  public String toString() {
  return "MyMessage [fromId=" + fromId + ", fromNickName=" + fromNickName + ", toId=" + toId + ", text=" + text
    + ", date=" + date + "]";
  }
}


二.三.三 封装在线用户列表 MyOnLineUserMap

package com.yjl.websocket.bean;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.socket.WebSocketSession;
import com.yjl.websocket.pojo.User;
/**
 * 封装在线用户列表信息
 * @author 两个蝴蝶飞
 *
 */
public class MyOnLineUserMap {
  //定义id 与 session的集合,用于发送消息
  private static Map<String,WebSocketSession> USER_ONLINE_SESSION_MAP;
  //定义id 与 user的集合,用于查询在线用户
  private static Map<String,User> USER_ONLINE_MAP;
  static {
  //初始化,长度为16
  USER_ONLINE_SESSION_MAP=new HashMap<String,WebSocketSession>(16);
  USER_ONLINE_MAP=new HashMap<String,User>(16);
  }
  public static Map<String, WebSocketSession> getUSER_ONLINE_SESSION_MAP() {
  return USER_ONLINE_SESSION_MAP;
  }
  public static Map<String, User> getUSER_ONLINE_MAP() {
  return USER_ONLINE_MAP;
  }
}


二.三.四 编写控制器 UserAction

用于跳转到登录,登录方法和查询在线用户列表的方法

package com.yjl.websocket.action;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.yjl.websocket.bean.MyOnLineUserMap;
import com.yjl.websocket.pojo.User;
/**
 * 
 * 处理用户跳转逻辑
 * @author 两个蝴蝶飞
 *
 */
@Controller
@RequestMapping("/User")
public class UserAction {
  /**
  * 跳转到登录的页面
  * @return
  */
  @RequestMapping("/toLogin")
  public String toLogin(HttpSession session){
  //需要清空session中的loginUser, 如果有的话
  if(session.getAttribute("loginUser")!=null){
    //移除
    session.removeAttribute("loginUser");
  }
  return "login";
  }
  /**
  * 登录操作
  * @param nickName
  * @param req
  * @param session
  * @return
  */
  @RequestMapping("/login")
  public String login(String nickName,HttpServletRequest req,HttpSession session){  
  //当前浏览器已经登录过了,那么就清空,保证每一个浏览器只能登录一个用户。
  if(session.getAttribute("loginUser")!=null){
    //移除
    session.removeAttribute("loginUser");
  }
  //编号为uuid
  User user=new User();
  user.setId(UUID.randomUUID().toString());
  user.setNickName(nickName);
  //放置到session 里面
  session.setAttribute("loginUser",user);
  System.out.println("**********新用户nickeName["+nickName+"]登录*****************");
  return "redirect:toMain.action";
  }
  /**
  * 跳转到主页
  * @param session
  * @return
  */
  @RequestMapping("/toMain")
  public String toMain(HttpSession session){
  //如果未登录,就跳转到登录页面
  if(session.getAttribute("loginUser")==null){
    return "redirect:toLogin";
  }
  return "main";
  }
  /**
  * 查询在线用户列表
  * @return
  */
  @RequestMapping(value="/onlineList")
  @ResponseBody
  public Map<String,Object> getOnlineUserList(){
  Map<String,Object> resultMap=new HashMap<String,Object>();
  List<User> allList=new ArrayList<User>();
  allList.addAll(MyOnLineUserMap.getUSER_ONLINE_MAP().values());
  resultMap.put("onlineList",allList);
  return resultMap;
  }
}


上面,都是正常的逻辑操作,与WebSocket 无关。


可以发现,Spring 整合 WebSocket 时,没有很大的侵入性,是松耦合的。


二.四 WebSocket的三大件编写

基本与 一.二 讲解的内容差不多。


二.四.一 编写拦截器 MyHandshakeInterceptor

package com.yjl.websocket.websocket;
import java.util.Map;
import javax.servlet.http.HttpSession;
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 com.yjl.websocket.pojo.User;
/**
 * 配置拦截器,需要继承 HandshakeInterceptor
 * @author 两个蝴蝶飞
 *
 */
@Component("myHandshakeInterceptor")
public class MyHandshakeInterceptor implements HandshakeInterceptor{
  /**
  * 先发送一个请求,请求连接
  */
  @Override
  public boolean beforeHandshake(ServerHttpRequest req, ServerHttpResponse resp, WebSocketHandler handler,
    Map<String, Object> attribute) throws Exception {
  if(req instanceof ServletServerHttpRequest){
    System.out.println("属于ServletServerHttpRequest");
    //先进行转换
    ServletServerHttpRequest servletRequest=(ServletServerHttpRequest)req;
    //得到Session
    HttpSession session=servletRequest.getServletRequest().getSession(false);
    //取出里面的 loginUser 的登录用户
    if(session.getAttribute("loginUser")!=null){
    User user=(User)session.getAttribute("loginUser");
    //放置到 map里面,这个map是 WebSocketSession的对象
    attribute.put("loginUser",user);
    System.out.println("连接一个新用户:[id:"+user.getId()+",nickName:"+user.getNickName());
    }else{
    System.out.println("***********用户未登录,握手失败*****************");
    return false;
    }
  }else{
    System.out.println("不属于ServletServerHttpRequest");
  }
  System.out.println("*********发送请求握手*************");
  return true;
  }
  /**
  * 请求连接成功
  */
  @Override
  public void afterHandshake(ServerHttpRequest arg0, ServerHttpResponse arg1, WebSocketHandler arg2, Exception arg3) {
  System.out.println("*********握手成功*************");
  }
}


二.四.二 编写处理器 MyWebSocketHandler

package com.yjl.websocket.websocket;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.util.HtmlUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yjl.websocket.bean.MyMessage;
import com.yjl.websocket.bean.MyOnLineUserMap;
import com.yjl.websocket.pojo.User;
/**
 * 配置处理器
 * @author 两个蝴蝶飞
 *
 */
@Component("myWebSocketHandler")
public class MyWebSocketHandler implements WebSocketHandler{
  /**
  * 当连接成功之后,进行的处理操作,对应 @OnOpen
  * wsSession 指的是 连接的那个浏览器用户信息
  */
  @Override
  public void afterConnectionEstablished(WebSocketSession wsSession) throws Exception {
  System.out.println("进来了:onOpen");
  //获取存于attribute的那个map
  Map<String,Object> attributes=wsSession.getAttributes();
  //刚刚登录成功的那个user 信息
  User user=(User)attributes.get("loginUser");
  //将这个信息,放置到在线的map里面
  MyOnLineUserMap.getUSER_ONLINE_SESSION_MAP().put(user.getId(),wsSession);
  MyOnLineUserMap.getUSER_ONLINE_MAP().put(user.getId(), user);
  //构建消息 MyMessage
  MyMessage message=new MyMessage();
  message.setText("风骚的【"+user.getNickName()+"】进入了聊天室,大家欢迎");
  message.setDate(new Date());
  //构建TextMessage 对象,然后发送对象信息
  ObjectMapper objMapper=new ObjectMapper();
  String textResult=objMapper.writeValueAsString(message);
  System.out.println("输出消息内容:"+textResult.toString());
  TextMessage textMessage=new TextMessage(textResult);
  //发送消息给所有人
  sendMessageToAll(textMessage);
  }
  /**
  * 主动断开连接后的事件, 对应 @OnClose 
  */
  @Override
  public void afterConnectionClosed(WebSocketSession wsSession, CloseStatus closeStatus) throws Exception {
  System.out.println("进来了:onClose");
  //获取该 wsSession 对应的那个User 信息
  User closeUser=(User)wsSession.getAttributes().get("loginUser");
  //构建 Message
  MyMessage message=new MyMessage();
  message.setFromId(closeUser.getId());
  message.setFromNickName(closeUser.getNickName());
  message.setText("万众嘱目的【"+closeUser.getNickName()+"】有事先走了,大家继续聊...");
  message.setDate(new Date());
  //在线列表里面,去除掉这个人的信息
  MyOnLineUserMap.getUSER_ONLINE_SESSION_MAP().remove(closeUser.getId());
  MyOnLineUserMap.getUSER_ONLINE_MAP().remove(closeUser.getId());
  //信息移除
  wsSession.getAttributes().remove("loginUser");
  ObjectMapper objMapper=new ObjectMapper();
  String textResult=objMapper.writeValueAsString(message);
  TextMessage textMessage=new TextMessage(textResult);
  //发送消息给所有人
  sendMessageToAll(textMessage);
  }
  /**
  * 浏览器发送消息之后,进行的处理操作, 对应 @OnMessage
  */
  @Override
  public void handleMessage(WebSocketSession wsSession, WebSocketMessage<?> message) throws Exception {
  System.out.println("进来了:onMessage");
  // 接收的消息,长度如果是0,表示没有消息,直接返回
  if(message.getPayloadLength()==0){
    return ;
  }
  ObjectMapper objMapper=new ObjectMapper();
  MyMessage inputMessage=objMapper.readValue(message.getPayload().toString(),MyMessage.class);
  //设置日期
  inputMessage.setDate(new Date());
  //接收到的消息
  String inputMsg=inputMessage.getText();
  System.out.println("【"+inputMessage.getFromNickName()+"】发送的消息是:"+inputMsg);
  //将这个消息,进行转义
  String escapeHTML=HtmlUtils.htmlEscape(inputMsg);
  //重新设置转义好的字符串
  inputMessage.setText(escapeHTML);
  //定义Message
  TextMessage textMessage=new TextMessage(objMapper.writeValueAsString(inputMessage));
  //接收到的消息, 看是群发,还是私发
  if(inputMessage.getToId()==null||"-1".equals(inputMessage.getToId())){
    //是群发
    sendMessageToAll(textMessage);
  }else{
    //是私发
    sendMessageToOne(inputMessage.getToId(), textMessage);
  }
  }
  /**
  * 错误时的消息, 对应的是 @OnError
  */
  @Override
  public void handleTransportError(WebSocketSession wsSession, Throwable throwable) throws Exception {
  System.out.println("进来了:onError");
  //如果目前开启,那么执行关闭
  if(wsSession.isOpen()){
    wsSession.close();
  }
  //获取该 wsSession 对应的那个User 信息
  User closeUser=(User)wsSession.getAttributes().get("loginUser");
  //构建 Message
  MyMessage message=new MyMessage();
  message.setFromId(closeUser.getId());
  message.setFromNickName(closeUser.getNickName());
  message.setText("万众嘱目的【"+closeUser.getNickName()+"】退出聊天室");
  message.setDate(new Date());
  //在线列表里面,去除掉这个人的信息
  MyOnLineUserMap.getUSER_ONLINE_SESSION_MAP().remove(closeUser.getId());
  MyOnLineUserMap.getUSER_ONLINE_MAP().remove(closeUser.getId());
  //信息移除
  wsSession.getAttributes().remove("loginUser");
  ObjectMapper objMapper=new ObjectMapper();
  String textResult=objMapper.writeValueAsString(message);
  TextMessage textMessage=new TextMessage(textResult);
  //发送消息给所有人
  sendMessageToAll(textMessage);
  }
  /**
     * 是否支持处理拆分消息,返回true返回拆分消息
     */
  //是否支持部分消息:如果设置为true,那么一个大的或未知尺寸的消息将会被分割,并会收到多次消息(会通过多次调用方法handleMessage(WebSocketSession, WebSocketMessage). )
  //如果分为多条消息,那么可以通过一个api:org.springframework.web.socket.WebSocketMessage.isLast() 是否是某条消息的最后一部分。
  //默认一般为false,消息不分割
  @Override
  public boolean supportsPartialMessages() {
  return false;
  }
  /**
  * 发送给单个用户
  * @param toId 用户编号
  * @param textMessage 发送消息
  */
  private void sendMessageToOne(String toId,final TextMessage textMessage){
  //没有接收人,则发送给全部的在线用户
  if(toId==null){
    sendMessageToAll(textMessage);
  }
  WebSocketSession toSession=MyOnLineUserMap.getUSER_ONLINE_SESSION_MAP().get(toId);
  //如果不存在,或者是未开启
  if(toSession==null||!toSession.isOpen()){
    return ;
  }
  try {
    toSession.sendMessage(textMessage);
  } catch (IOException e) {
    // TODO 自动生成的 catch 块
    e.printStackTrace();
  }
  }
  /**
  * 发送给全部的用户
  * @param textMessage 发送消息
  */
  private void sendMessageToAll(final TextMessage textMessage) {
  //遍历所有的在线用户,包括自己
  for(Map.Entry<String,WebSocketSession> wsSession:MyOnLineUserMap.getUSER_ONLINE_SESSION_MAP().entrySet()){
    //获取 WebSocketSession
    WebSocketSession onLineSession=wsSession.getValue();
    //是打开的状态
    if(onLineSession.isOpen()){
    //开启线程
    new Thread(new Runnable() {
      @Override
      public void run() {
      if(onLineSession.isOpen()){
        //发送消息
        try {
        onLineSession.sendMessage(textMessage);
        System.out.println("发送消息成功");
        } catch (IOException e) {
        e.printStackTrace();
        }
      }
      }
    }).start();
    }
  }
  }
}


二.四.三 编写 注册工厂 WebSocketConfig

package com.yjl.websocket.websocket;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
/**
 * 注册处理器和拦截器
 * @author 两个蝴蝶飞
 *
 */
@Component(value="webSocketConfig")
//通过注解 EnableWebSocket 
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
  /**
  * 注册服务
  */
  @Override
  public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  registry.addHandler(new MyWebSocketHandler(),"/ws").addInterceptors(new MyHandshakeInterceptor());
  /*
   * 在这里我们用到.withSockJS(),SockJS是spring用来处理浏览器对websocket的兼容性,
   * 目前浏览器支持websocket还不是很好,特别是IE11以下.
   * SockJS能根据浏览器能否支持websocket来提供三种方式用于websocket请求,
   * 三种方式分别是 WebSocket, HTTP Streaming以及 HTTP Long Polling
   */
  registry.addHandler(new MyWebSocketHandler(),"ws/sockjs").addInterceptors(new MyHandshakeInterceptor())
  .withSockJS();
  } 
}


后端的处理,算是基本完成了。


二.五 处理前端的 main.jsp 页面

添加 js 脚本。 (不要忘记添加相应的 js 和样式表)


基本的东西,就不讲解了。

<script>
  var path='<%=basePath%>';
  //定义MyMessage 需要用到的属性信息
  //当前进入的id信息
  var uid="{sessionScope.loginUser.id}";
  var fromId=uid;
  var fromNickName='${sessionScope.loginUser.nickName}';
  //默认是-1, 表示全部接收
  var toId=-1;
  // 创建一个Socket实例
  //参数为URL,ws表示WebSocket协议。onopen、onclose和onmessage方法把事件连接到Socket实例上。每个方法都提供了一个事件,以表示Socket的状态。
  var webSocket;
  //不同浏览器的WebSocket对象类型不同
  if ('WebSocket' in window) {
  webSocket = new WebSocket("ws://" + path + "ws");
  //火狐
  } else if ('MozWebSocket' in window) {
  webSocket = new MozWebSocket("ws://" + path + "ws");
  } else {
  webSocket = new SockJS("http://" + path + "ws/sockjs");
  }
  //定义四个事件, onopen,onclose,onmessage,onerror
  //打开Socket,
  webSocket.onopen = function(event) { 
  //console.log("WebSocket:已连接");
  refreshOnLineList();
  }
  // 监听消息
  //onmessage事件提供了一个data属性,它可以包含消息的Body部分。消息的Body部分必须是一个字符串,可以进行序列化/反序列化操作,以便传递更多的数据。
  webSocket.onmessage = function(event) { 
  var data=JSON.parse(event.data);
  //console.log("WebSocket:收到一条消息",data);
  //2种推送的消息
  //1.用户聊天信息:发送消息触发
  //2.系统消息:登录和退出触发
  //判断是否是欢迎消息(没用户编号的就是欢迎消息)
  if(data.fromId==undefined||data.fromId==null||data.fromId==""){
    //===系统消息
    $("#contentUl").append("<li><b class='dateStyle'>"+data.date+"</b><em class='sysStyle'>系统消息:</em><span class='sysTextStyle'>"+data.text+"</span></li>");
  }else{
    //===普通消息
    //处理一下个人信息的显示:
    if(data.fromNickName==fromNickName){
    data.fromNickName="我 :";
    $("#contentUl").append("<li><span  style='display:block; float:right;'><em class='nickNameStyle'>"+data.fromNickName+"</em><span class='textStyle'>"+data.text+"</span><b class='dateStyle'>"+data.date+"</b></span></li><br/>");
    }else{
    $("#contentUl").append("<li><b class='dateStyle'>"+data.date+"</b><em class='nickNameStyle'>"+data.fromNickName+"</em><span class='textStyle'>"+data.text+"</span></li><br/>");
    }
  }
  //刷新在线用户列表
  refreshOnLineList();
  scrollToBottom();
  }; 
  // 监听WebSocket的关闭
  webSocket.onclose = function(event) { 
  refreshOnLineList();
  $("#contentUl").append("<li><b>"+new Date().Format("yyyy-MM-dd hh:mm:ss")+"</b><em>系统消息:</em><span>连接已断开!</span></li>");
  scrollToBottom();
  }; 
  //监听异常
  webSocket.onerror = function(event) {
  refreshOnLineList();
  $("#contentUl").append("<li><b>"+new Date().Format("yyyy-MM-dd hh:mm:ss")+"</b><em>系统消息:</em><span>连接异常,建议重新登录</span></li>");
  scrollToBottom();
  };
  //onload初始化
  $(function(){
  //发送消息
  $("#sendBtn").on("click",function(){
    sendMsg();
  });
  //给退出聊天绑定事件
  $("#exitBtn").on("click",function(){
    closeWebsocket();
    //跳转到主页
    location.href="${pageContext.request.contextPath}/index.jsp";
  });
  //给输入框绑定事件
  $("#msg").on("keydown",function(event){
    keySend(event);
  });
  //初始化时如果有消息,则滚动条到最下面:
  scrollToBottom();
  });
  //使用ctrl+回车快捷键发送消息
  function keySend(e) {
  var theEvent = window.event || e; 
  var code = theEvent.keyCode || theEvent.which; 
  if (theEvent.ctrlKey && code == 13) {
    var msg=$("#msg");
    if (msg.innerHTML == "") {
    msg.focus();
    return false;
    }
    sendMsg();
  }
  }
  //发送消息
  function sendMsg(){
  //对象为空了
  if(webSocket==undefined||webSocket==null){
    //alert('WebSocket connection not established, please connect.');
    alert('您的连接已经丢失,请退出聊天重新进入');
    return;
  }
  //获取用户要发送的消息内容
  var msg=$("#msg").val();
  if(msg==""){
    return;
  }else{
    var data={};
    data["fromId"]=fromId;
    data["fromNickName"]=fromNickName;
    data["toId"]=toId;
    data["text"]=msg;
    //发送消息
    webSocket.send(JSON.stringify(data));
    //发送完消息,清空输入框
    $("#msg").val("");
  }
  }
  //关闭Websocket连接
  function closeWebsocket(){
  if (webSocket != null) {
    webSocket.close();
    webSocket = null;
  }
  }
  //div滚动条(scrollbar)保持在最底部
  function scrollToBottom(){
  //var div = document.getElementById('chatCon');
  var div = document.getElementById('up');
  div.scrollTop = div.scrollHeight;
  } 
  //格式化日期
  Date.prototype.Format = function (fmt) { //author: meizz 
     var o = {
         "M+": this.getMonth() + 1, //月份 
         "d+": this.getDate(), //日 
         "h+": this.getHours(), //小时 
         "m+": this.getMinutes(), //分 
         "s+": this.getSeconds(), //秒 
         "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
         "S": this.getMilliseconds() //毫秒 
     };
     if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
     for (var k in o)
     if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
     return fmt;
  }
  /*
  刷新在线用户列表
  */
  function refreshOnLineList(){
  $.ajax({
         type : "post",
         url : "../User/onlineList",
         dataType : "json",
         data : {} ,
         success : function(data) {
             var onlineList=data.onlineList;
             //有值的话 
             if(onlineList){
              $("#onlineNum").text(onlineList.length);
              $("#online").empty();
               $.each(onlineList,function(idx,item){
                var $li=$("<li><a href='javascript:void(0);' data-id='"+item.id+"'>"+item.nickName+"</a></li>");
                $("#online").append($li); 
               })
              addAClickEvent($("#online li a"));
             }
         }
     });
  /*
    点击私聊事件,暂未处理
  */
  function addAClickEvent(target){
    target.click(function(){
    var clickId=target.attr("data-id");
    if(clickId==fromId){
      alert("自己不能跟自己聊天");
      return ;
    }
    alert("你要私聊的人的id是:"+clickId);
    //打开模态框,输入私聊的信息,进行私聊。 
    //不在讲解范围之内,可看后续的聊天室项目。
    return ;
    })
  }
  } 
</script>


三. 运行服务器,测试

火狐浏览器打开网址: http://localhost:8080/chatroom/


输入昵称,“两个蝴蝶飞”

image.png



点击进入


image.png


谷歌浏览器打开网址: http://localhost:8080/chatroom/


输入昵称, “岳泽霖”

image.png



点击进入


image.png


这个时候, 两个蝴蝶飞的火狐浏览器显示:

image.png



经上一章节的各种测试行为,包括退出登录等,服务器均可以推送消息到客户端, WebSocket 功能整合成功。

相关文章
|
2月前
|
前端开发 网络协议 Java
【spring(六)】WebSocket网络传输协议
【spring(六)】WebSocket网络传输协议
|
7月前
|
前端开发 Java 程序员
Spring Boot+Netty+Websocket实现后台向前端推送信息
学过 Netty 的都知道,Netty 对 NIO 进行了很好的封装,简单的 API,庞大的开源社区。深受广大程序员喜爱。基于此本文分享一下基础的 netty 使用。实战制作一个 Netty + websocket 的消息推送小栗子。
|
消息中间件 前端开发 JavaScript
Spring Boot+Netty+Websocket实现后台向前端推送信息
Spring Boot+Netty+Websocket实现后台向前端推送信息
|
7月前
|
前端开发 Cloud Native Java
使用Spring WebSocket实现实时通信功能
使用Spring WebSocket实现实时通信功能
65 0
|
8月前
|
存储 安全 JavaScript
Spring WebSocket 认证与授权:掌控安全通道,迈向巅峰之旅!
- http和WebSocket的安全链和安全配置是完全独立的。 - SpringAuthenticationProvider根本不参与 Websocket 身份验证。 - 将要给出的示例中,身份验证不会发生在 HTTP 协商端点上,因为 JavaScript STOMP(websocket)库不会随 HTTP 请求一起发送必要的身份验证标头。 - 一旦在 CONNECT 请求上设置,用户( simpUser) 将被存储在 websocket 会话中,并且以后的消息将不再需要进行身份验证
228 1
|
9月前
|
Java API Spring
在SockJS+Spring Websocket中convertAndSendToUser中的“用户”来自哪里?
我们知道可以使用客户端订阅的主题前缀从 stomp 服务器向客户端发送消息,例如 /topic/hello。我们还知道我们可以向特定用户发送消息,因为 spring 提供了convertAndSendToUser(username, destination, message)API。它接受一个字符串用户名,这意味着如果我们以某种方式为每个连接都有一个唯一的用户名,我们能够向订阅某个主题的特定用户发送消息。 那么,这个用户名来自哪里?或者说它是如何用这个用户名确定对应连接的?
114 0
|
9月前
|
存储 安全 JavaScript
Spring中的Websocket身份验证和授权
- http和WebSocket的安全链和安全配置是完全独立的。 - SpringAuthenticationProvider根本不参与 Websocket 身份验证。 - 将要给出的示例中,身份验证不会发生在 HTTP 协商端点上,因为 JavaScript STOMP(websocket)库不会随 HTTP 请求一起发送必要的身份验证标头。 - 一旦在 CONNECT 请求上设置,用户( simpUser) 将被存储在 websocket 会话中,并且以后的消息将不再需要进行身份验证。
200 0
BXA
|
12月前
|
监控 网络协议 JavaScript
Spring WebSocket构建实时交互系统
Spring WebSocket是基于WebSocket协议的一个开源框架,它使得开发人员可以更加方便地建立实时通信机制,以推送消息和数据并实时更新通信系统中的状态。Spring WebSocket被广泛应用于社交网站、电子商务、在线游戏等WEB应用程序中,以实现实时通信和即时响应。
BXA
146 0
|
监控 前端开发 Java
Spring Boot + WebSocket 实时监控异常
此异常非彼异常,标题所说的异常是业务上的异常。 最近做了一个需求,消防的设备巡检,如果巡检发现异常,通过手机端提交,后台的实时监控页面实时获取到该设备的信息及位置,然后安排员工去处理。 因为需要服务端主动向客户端发送消息,所以很容易的就想到了用WebSocket来实现这一功能。
327 0
|
Java 容器 Spring
Spring Boot---使用@ServerEndpoint创建WebSocket
Spring Boot---使用@ServerEndpoint创建WebSocket
Spring Boot---使用@ServerEndpoint创建WebSocket