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 功能整合成功。

相关文章
|
6月前
|
设计模式 Java Spring
【Spring源码】WebSocket做推送动作的底层实例是谁
我们都知道WebSocket可以主动推送消息给用户,那做推送动作的底层实例究竟是谁?我们先整体看下整个模块的组织机构。可以看到handleMessage方法定义了每个消息格式采用不同的消息处理方法,而这些方法该类并**没有实现**,而是留给了子类去实现。
【Spring源码】WebSocket做推送动作的底层实例是谁
|
4月前
|
前端开发 网络协议 JavaScript
在Spring Boot中实现基于WebSocket的实时通信
在Spring Boot中实现基于WebSocket的实时通信
|
24天前
|
开发框架 前端开发 网络协议
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
246 1
|
29天前
|
前端开发 Java C++
RSocket vs WebSocket:Spring Boot 3.3 中的两大实时通信利器
本文介绍了在 Spring Boot 3.3 中使用 RSocket 和 WebSocket 实现实时通信的方法。RSocket 是一种高效的网络通信协议,支持多种通信模式,适用于微服务和流式数据传输。WebSocket 则是一种标准协议,支持全双工通信,适合实时数据更新场景。文章通过一个完整的示例,展示了如何配置项目、实现前后端交互和消息传递,并提供了详细的代码示例。通过这些技术,可以大幅提升系统的响应速度和处理效率。
|
2月前
|
JavaScript 前端开发 Java
【颠覆传统】Spring框架如何用WebSocket技术重塑实时通信格局?揭秘背后的故事与技术细节!
【9月更文挑战第4天】随着Web应用对实时交互需求的增长,传统的HTTP模型已无法满足现代应用的要求,特别是在需要持续、双向通信的场景下。WebSocket协议由此诞生,提供全双工通信渠道,使服务器与客户端能实时互发消息。作为Java开发中最受欢迎的框架之一,Spring通过其WebSocket模块支持这一协议,简化了WebSocket在Spring应用中的集成。
55 0
|
3月前
|
JavaScript 前端开发 网络协议
WebSocket在Java Spring Boot+Vue框架中实现消息推送功能
在现代Web应用中,实时消息提醒是一项非常重要的功能,能够极大地提升用户体验。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为实现实时消息提醒提供了高效且低延迟的解决方案。本文将详细介绍如何在Java Spring Boot后端和Vue前端框架中利用WebSocket实现消息提醒功能。
157 0
|
5月前
|
前端开发 JavaScript 安全
集成WebSocket在Spring Boot中可以用于实现实时的双向通信
集成WebSocket在Spring Boot中可以用于实现实时的双向通信
86 4
|
4月前
|
监控 网络协议 Java
如何在Spring Boot中使用WebSocket
如何在Spring Boot中使用WebSocket
|
4月前
|
监控 前端开发 网络协议
如何使用Spring Boot实现WebSocket通信
如何使用Spring Boot实现WebSocket通信
|
6月前
|
网络协议 前端开发 Java
Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)
Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)
66 4