web即时通信2--基于Spring websocket实现web聊天室

转自:http://blog.csdn.net/jrn1012/article/details/42079821

   文中代码部分参考了Spring WebSocket教程(一)Spring WebSocket教程(二)。

  本文使用Spring4和websocket搭建一个web聊天室,框架基于SpringMVC+Spring+Hibernate的Maven项目,后台使用spring websocket进行消息转发和聊天消息缓存。客户端使用socket.js和stomp.js来进行消息订阅和消息发送。详细实现见下面代码。

         首先在pom.xml中添加对spring websocket的相关依赖包。

         一、添加websocket依赖

[html]  view plain copy
  1. <span style="font-size:14px;">             <dependency>  
  2.             <groupId>org.springframework</groupId>  
  3.             <artifactId>spring-webmvc</artifactId>  
  4.             <version>${springframework.version}</version>  
  5.         </dependency>  
  6.         <dependency>  
  7.             <groupId>org.springframework</groupId>  
  8.             <artifactId>spring-websocket</artifactId>  
  9.             <version>${springframework.version}</version>  
  10.         </dependency>  
  11.         <dependency>  
  12.             <groupId>org.springframework</groupId>  
  13.             <artifactId>spring-messaging</artifactId>  
  14.             <version>${springframework.version}</version>  
  15.         </dependency>  
  16.         <dependency>  
  17.             <groupId>org.apache.poi</groupId>  
  18.             <artifactId>poi</artifactId>  
  19.             <version>3.9</version>  
  20.         </dependency>  
  21.              <dependency>    
  22.                 <groupId>com.fasterxml.jackson.core</groupId>    
  23.                 <artifactId>jackson-core</artifactId>    
  24.                 <version>2.3.0</version>    
  25.             </dependency>    
  26.             <dependency>    
  27.                 <groupId>com.fasterxml.jackson.core</groupId>    
  28.                 <artifactId>jackson-databind</artifactId>    
  29.                 <version>2.3.0</version>    
  30.             </dependency>    
  31.             <dependency>    
  32.                 <groupId>com.fasterxml.jackson.core</groupId>    
  33.                 <artifactId>jackson-annotations</artifactId>    
  34.                 <version>2.3.0</version>    
  35.             </dependency>     
  36. </span>  

       其中<springframework.version>4.0.3.RELEASE</springframework.version>。因为spring4以上才支持WebSocket。

        2、配置Spring WebSocket

        该配置可以在Spring MVC的配置文件配置,也可以使用注解方式配置,本文使用注解@Configuration方式进行配置。

[html]  view plain copy
  1. <span style="font-size:14px;">package com.test.chat.controller;  
  2.   
  3. import java.util.List;  
  4.   
  5. import org.springframework.context.annotation.Configuration;  
  6. import org.springframework.messaging.converter.MessageConverter;  
  7. import org.springframework.messaging.simp.config.ChannelRegistration;  
  8. import org.springframework.messaging.simp.config.MessageBrokerRegistry;  
  9. import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;  
  10. import org.springframework.web.socket.config.annotation.StompEndpointRegistry;  
  11. import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;  
  12. import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;  
  13.   
  14. @Configuration  
  15. @EnableWebSocketMessageBroker  
  16. public class WebSocketConfig implements WebSocketMessageBrokerConfigurer{  
  17.     @Override  
  18.     public void registerStompEndpoints(StompEndpointRegistry registry) {  
  19.         //添加这个Endpoint,这样在网页中就可以通过websocket连接上服务了  
  20.         registry.addEndpoint("/webchat").withSockJS();  
  21.     }  
  22.        
  23.     @Override  
  24.     public void configureMessageBroker(MessageBrokerRegistry config) {  
  25.         System.out.println("服务器启动成功");  
  26.         //这里设置的simple broker是指可以订阅的地址,也就是服务器可以发送的地址  
  27.         config.enableSimpleBroker("/userChat","/initChat","/initFushionChart","/updateChart","/videoChat");    
  28.         config.setApplicationDestinationPrefixes("/app");     
  29.     }  
  30.   
  31.     @Override  
  32.     public void configureClientInboundChannel(ChannelRegistration channelRegistration) {  
  33.     }  
  34.   
  35.     @Override  
  36.     public void configureClientOutboundChannel(ChannelRegistration channelRegistration) {  
  37.     }  
  38.   
  39.     @Override  
  40.     public void configureWebSocketTransport(  
  41.             WebSocketTransportRegistration registry) {  
  42.         // TODO Auto-generated method stub  
  43.         System.out.println("registry:"+registry);  
  44.     }  
  45.   
  46.     @Override  
  47.     public boolean configureMessageConverters(  
  48.             List<MessageConverter> messageConverters) {  
  49.         // TODO Auto-generated method stub  
  50.         System.out.println("messageConverters:"+messageConverters);  
  51.         return true;  
  52.     }  
  53. }  
  54. </span>  

    要使配置文件生效,需在Spring的文件中能够扫描到该文件所在的包。即配置<context:component-scan base-package="com.test.**.controller" />

       3、聊天内容的实体对象和后台关键代码

[html]  view plain copy
  1. <span style="font-size:14px;">package com.test.chat.model;  
  2.   
  3. public class ChatMessage {  
  4.     //房间号  
  5.     private String roomid;  
  6.     //用户名  
  7.     private String userName;  
  8.     //机构名  
  9.     private String deptName;  
  10.     //当前系统时间  
  11.     private String curTime;  
  12.     //聊天内容  
  13.     private String chatContent;  
  14.     //是否是系统消息  
  15.     private String isSysMsg;  
  16.       
  17.     public String getIsSysMsg() {  
  18.         return isSysMsg;    
  19.     }  
  20.     public void setIsSysMsg(String isSysMsg) {  
  21.         this.isSysMsg = isSysMsg;  
  22.     }  
  23.     public String getRoomid() {  
  24.         return roomid;  
  25.     }  
  26.     public void setRoomid(String roomid) {  
  27.         this.roomid = roomid;  
  28.     }  
  29.     public String getUserName() {  
  30.         return userName;  
  31.     }  
  32.     public void setUserName(String userName) {  
  33.         this.userName = userName;  
  34.     }  
  35.     public String getDeptName() {  
  36.         return deptName;  
  37.     }  
  38.     public void setDeptName(String deptName) {  
  39.         this.deptName = deptName;  
  40.     }  
  41.     public String getCurTime() {  
  42.         return curTime;  
  43.     }  
  44.     public void setCurTime(String curTime) {  
  45.         this.curTime = curTime;  
  46.     }  
  47.     public String getChatContent() {  
  48.         return chatContent;  
  49.     }  
  50.     public void setChatContent(String chatContent) {  
  51.         this.chatContent = chatContent;  
  52.     }  
  53.   
  54. }  
  55. </span>  
  

    后台关键处理代码,用于转发消息并缓存聊天记录

[html]  view plain copy
  1. <span style="font-size:14px;">package com.test.chat.controller;  
  2.   
  3. import java.io.UnsupportedEncodingException;  
  4. import java.net.URLDecoder;  
  5. import java.util.Date;  
  6. import java.util.HashMap;  
  7. import java.util.Map;  
  8.   
  9. import javax.annotation.Resource;  
  10. import javax.servlet.http.HttpSession;  
  11.   
  12. import org.springframework.beans.factory.annotation.Autowired;  
  13. import org.springframework.messaging.handler.annotation.DestinationVariable;  
  14. import org.springframework.messaging.handler.annotation.MessageMapping;  
  15. import org.springframework.messaging.simp.SimpMessagingTemplate;  
  16. import org.springframework.messaging.simp.annotation.SubscribeMapping;  
  17. import org.springframework.stereotype.Controller;  
  18. import org.springframework.ui.Model;  
  19. import org.springframework.web.bind.annotation.RequestMapping;  
  20.   
  21. import com.alibaba.fastjson.JSONObject;  
  22. import com.test.chat.model.ChatMessage;  
  23. import com.test.chat.model.LimitQueue;  
  24. import com.test.chat.model.VideoMessage;  
  25. import com.test.framework.common.SessionContainer;  
  26. import com.test.framework.service.GenericService;  
  27. import com.test.framework.utils.DateUtil;  
  28.   
  29. @Controller  
  30. public class UserChatController {  
  31.         //每个聊天室缓存最大聊天信息条数,该值由SpringMVC的配置文件注入,超过该值将清理出缓存  
  32.     private int MAX_CHAT_HISTORY;  
  33.   
  34.     public void setMAX_CHAT_HISTORY(int MAX_CHAT_HISTORY) {  
  35.         this.MAX_CHAT_HISTORY = MAX_CHAT_HISTORY;  
  36.     }  
  37.   
  38.     @Resource  
  39.     private GenericService genericService;  
  40.     // 用于转发数据 sendTo  
  41.     private SimpMessagingTemplate template;  
  42.         //消息缓存列表  
  43.         private Map<String, Object> msgCache = new HashMap<String, Object>();  
  44.   
  45.     @Autowired  
  46.     public UserChatController(SimpMessagingTemplate t) {  
  47.         template = t;  
  48.     }  
  49.   
  50.   
  51.     /**  
  52.      * WebSocket聊天的相应接收方法和转发方法  
  53.      * 客户端通过app/userChat调用该方法,并将处理的消息发送客户端订阅的地址  
  54.      * @param userChat     关于用户聊天的各个信息  
  55.      */  
  56.     @MessageMapping("/userChat")  
  57.     public void userChat(ChatMessage chatMessage) {  
  58.         // 找到需要发送的地址(客户端订阅地址)  
  59.         String dest = "/userChat/chat" + chatMessage.getRoomid();  
  60.         // 获取缓存,并将用户最新的聊天记录存储到缓存中  
  61.         Object cache = msgCache.get(chatMessage.getRoomid());  
  62.         try {  
  63.             chatMessage.setRoomid(URLDecoder.decode(chatMessage.getRoomid(),"utf-8"));  
  64.             chatMessage.setUserName(URLDecoder.decode(chatMessage.getUserName(), "utf-8"));  
  65.             chatMessage.setDeptName(URLDecoder.decode(chatMessage.getDeptName(), "utf-8"));  
  66.             chatMessage.setChatContent(URLDecoder.decode(chatMessage.getChatContent(), "utf-8"));  
  67.             chatMessage.setIsSysMsg(URLDecoder.decode(chatMessage.getIsSysMsg(),"utf-8"));  
  68.             chatMessage.setCurTime(DateUtil.format(new Date(),DateUtil.formatStr_yyyyMMddHHmmss));  
  69.         } catch (UnsupportedEncodingException e) {  
  70.             e.printStackTrace();   
  71.         }  
  72.         // 发送用户的聊天记录  
  73.         this.template.convertAndSend(dest, chatMessage);  
  74.                 ((LimitQueue<ChatMessage>) cache).offer(chatMessage);  
  75.     }  
  76.   
  77.       
  78.     @SubscribeMapping("/initChat/{roomid}")  
  79.     public LimitQueue<ChatMessage> initChatRoom(@DestinationVariable String roomid) {  
  80.         System.out.print("-------新用户进入聊天室------");  
  81.         LimitQueue<ChatMessage> chatlist = new LimitQueue<ChatMessage>(MAX_CHAT_HISTORY);  
  82.         // 发送用户的聊天记录  
  83.         if (!msgCache.containsKey(roomid)) {  
  84.             // 从来没有人进入聊天空间  
  85.             msgCache.put(roomid, chatlist);  
  86.         } else {  
  87.             chatlist = (LimitQueue<ChatMessage>) msgCache.get(roomid);  
  88.         }  
  89.         return chatlist;  
  90.     }  
  91.       
  92. }  
  93. </span>  
在Spring的配置文件中注入MAX_CHAT_HISTRORY
[html]  view plain copy
  1. <span style="font-size:14px;">    <bean id="userChatController" class="com.test.chat.controller.UserChatController">  
  2.         <property name="MAX_CHAT_HISTORY" value="20"/>  
  3.     </bean>       </span>  

其中缓存队列LimitQueue的实现为:
[html]  view plain copy
  1. <span style="font-size:14px;">package com.test.chat.model;  
  2.   
  3. import java.util.Collection;  
  4. import java.util.Iterator;  
  5. import java.util.LinkedList;  
  6. import java.util.Queue;  
  7.   
  8. public class LimitQueue<E> implements Queue<E> {  
  9.     private int limit;  
  10.     private Queue<E> queue;  
  11.   
  12.     public LimitQueue(int limit) {  
  13.         this.limit = limit;  
  14.         this.queue = new LinkedList<E>();  
  15.     }  
  16.   
  17.     @Override  
  18.     public int size() {  
  19.         return queue.size();  
  20.     }  
  21.   
  22.     @Override  
  23.     public boolean isEmpty() {  
  24.         return queue.isEmpty();  
  25.     }  
  26.   
  27.     @Override  
  28.     public boolean contains(Object o) {  
  29.         return queue.contains(o);  
  30.     }  
  31.   
  32.     @Override  
  33.     public Iterator<E> iterator() {  
  34.         return queue.iterator();  
  35.     }  
  36.   
  37.     @Override  
  38.     public Object[] toArray() {  
  39.         return queue.toArray();  
  40.     }  
  41.   
  42.     @Override  
  43.     public <T> T[] toArray(T[] a) {  
  44.         return queue.toArray(a);  
  45.     }  
  46.   
  47.     @Override  
  48.     public boolean add(E e) {  
  49.         return queue.add(e);  
  50.     }  
  51.   
  52.     @Override  
  53.     public boolean remove(Object o) {  
  54.         return queue.remove(0);  
  55.     }  
  56.   
  57.     @Override  
  58.     public boolean containsAll(Collection<?> c) {  
  59.         return queue.containsAll(c);  
  60.     }  
  61.   
  62.     @Override  
  63.     public boolean addAll(Collection<? extends E> c) {  
  64.         return queue.addAll(c);  
  65.     }  
  66.   
  67.     @Override  
  68.     public boolean removeAll(Collection<?> c) {  
  69.         return queue.removeAll(c);  
  70.     }  
  71.   
  72.     @Override  
  73.     public boolean retainAll(Collection<?> c) {  
  74.         return queue.retainAll(c);  
  75.     }  
  76.   
  77.     @Override  
  78.     public void clear() {  
  79.         queue.clear();  
  80.     }  
  81.   
  82.     @Override  
  83.     public boolean offer(E e) {  
  84.         if (queue.size() >= limit) {  
  85.             queue.poll();  
  86.         }  
  87.         return queue.offer(e);  
  88.     }  
  89.   
  90.     @Override  
  91.     public E remove() {  
  92.         return queue.remove();  
  93.     }  
  94.   
  95.     @Override  
  96.     public E poll() {  
  97.         return queue.poll();  
  98.     }  
  99.   
  100.     @Override  
  101.     public E element() {  
  102.         return queue.element();  
  103.     }  
  104.   
  105.     @Override  
  106.     public E peek() {  
  107.         return queue.peek();  
  108.     }  
  109.   
  110.     public int getLimit() {  
  111.         return this.limit;  
  112.     }  
  113. }  
  114. </span>  

         四、前台聊天室的实现(前台界面使用dhtmlx控件)

[html]  view plain copy
  1. <span style="font-size:14px;"><%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  3. <html>  
  4.     <head>  
  5.         <title>聊天室管理</title>  
  6.         <meta http-equiv="content-type" content="text/html; charset=utf-8" />   
  7.         <link rel="stylesheet" type="text/css" href="/dhtmlx/dhtmlxEditor/codebase/skins/dhtmlxeditor_dhx_skyblue.css">  
  8.         <script src="/common/js/lib-base.js" type="text/javascript"></script>   
  9.         <script src="/dhtmlx/dhtmlxEditor/codebase/dhtmlxeditor.js" type="text/javascript"></script>  
  10.         <!-- <script src="/dhtmlx/dhtmlxEditor/codebase/ext/dhtmlxeditor_ext.js" type="text/javascript"></script> -->  
  11.         <!-- web chat 引入相关脚本 -->  
  12.         <script src="/common/js/websocket/sockjs-0.3.4.min.js" type="text/javascript"></script>  
  13.         <script src="/common/js/websocket/stomp.js" type="text/javascript"></script>  
  14.         <!----------------end------------------->  
  15.         <script>    
  16.     var chatLayout;   
  17.     var roomid="${roomid}";    
  18.     var roomName=null;  
  19.     var friendTree=null;  
  20.     var userid=null;  
  21.     var username=null;   
  22.     var deptSortName=null;  
  23.     var editor=null;  
  24.     $(function(){     
  25.         commonForm.initForm();   
  26.         chatLayoutnew dhtmlXLayoutObject(document.body, "3J");  
  27.         ajaxPost("/chatroom/findById",{"id":roomid},function(data,status){  
  28.             chatLayout.cells("a").setText("<img src='/images/Pub/User.gif' width='16px' height='16px' align='absmiddle' style='margin-right:5px'>"+data.roomName);  
  29.             roomName=data.roomName;  
  30.             $("#roomRemark").html(data.remark);   
  31.         }) ;    
  32.         chatLayout.cells("a").hideHeader();   
  33.         chatLayout.cells("a").attachObject("chatMsg");  
  34.         chatLayout.cells("c").setHeight(150);   
  35.         chatLayout.cells("c").hideHeader();       
  36.         chatLayout.setAutoSize("a;c","a;b");     
  37.         chatLayout.cells("b").setWidth(180);   
  38.         var friendLayout=chatLayout.cells("b").attachLayout("2E");   
  39.         friendTree=friendLayout.cells("b").attachTree();   
  40.         friendLayout.cells("a").setText("<img src='/images/Pub/User.gif' width='16px' height='16px' align='absmiddle' style='margin-right:5px'>群公告");  
  41.         friendLayout.cells("a").attachObject("roomRemark");  
  42.         friendLayout.cells("a").setHeight(100);  
  43.         friendLayout.cells("b").setText("<img src='/images/Pub/User.gif' width='16px' height='16px' align='absmiddle' style='margin-right:5px'>好友列表");  
  44.         friendLayout.setAutoSize("a;b","b");    
  45.         //加载好友列表树   
  46.         ajaxPost("/auth/getCurUser",null,function(data,status){  
  47.             userid=data.id;  
  48.             username=data.name;  
  49.             deptSortName=data.deptSortName;  
  50.         })   
  51.         loadChatFriend();  
  52.         //加载聊天  
  53.         var talkLayout=chatLayout.cells("c").attachLayout("2E");  
  54.         talkLayout.cells("a").hideHeader();  
  55.         talkLayout.cells("b").hideHeader();  
  56.         talkLayout.cells("b").setHeight(29);   
  57.         //dhtmlx.image_path="/dhtmlx/dhtmlxEditor/codebase/imgs/";     
  58.         editor=talkLayout.cells("a").attachEditor();   
  59.         var toolbar=talkLayout.cells("b").attachToolbar();  
  60.         toolbar.setIconsPath("/images/Pub/");  
  61.         var tbindex=0;   
  62.         toolbar.addSeparator("sep1", tbindex++);    
  63.         toolbar.addSpacer("sep1");   
  64.         toolbar.addButton("closeChat", tbindex++, "关闭", "delete.png","delte.png");  
  65.         toolbar.addSeparator("sep2",tbindex++);    
  66.         toolbar.addButton("videoChat", tbindex++, "视频", "FrameReLogin.gif","FrameReLogin.gif");  
  67.         toolbar.addSeparator("sep3",tbindex++);    
  68.         toolbar.addButton("sendMessage", tbindex++, "发送", "redo.gif","redo.gif");  
  69.           
  70.         toolbar.attachEvent("onclick",function(tid){  
  71.             switch(tid){  
  72.                 case "sendMessage":  
  73.                     if(editor.getContent()=="" )  
  74.                         return;  
  75.                     sendMessage("0");  
  76.                     editor.setContent("");     
  77.                     break;   
  78.                 case "closeChat":    
  79.                     sendMessage("1","离开");   
  80.                     parent.dhxWins.window("chatWin").close();  
  81.                     break;    
  82.                 case "videoChat":   
  83.                     top.openWindow("/video/openVideoChat?roomid="+roomid,"videoWin","聊天室【"+roomName+"】",650,550,false,false,true);  
  84.                     break;   
  85.                 default:   
  86.                     break;   
  87.             }   
  88.         })     
  89.              
  90.           
  91.     });    
  92.       
  93.     function loadChatFriend(){  
  94.          friendTree.setSkin('dhx_skyblue');     
  95.          friendTree.setImagePath("/dhtmlx/dhtmlxTree/codebase/imgs/csh_dhx_skyblue/");  
  96.          ajaxPost("/chatroom/getChatFriends",{"roomid":roomid},function(data,status){  
  97.              friendTree.deleteChildItems(friendTree.rootId);   
  98.              $.each(data,function(index,item){  
  99.                  var id=item.user.id;  
  100.                  var deptName=item.user.corg.shortName;  
  101.                  var userName=item.user.name;  
  102.                  var isCreator=item.isCreator;  
  103.                  friendTree.insertNewItem(friendTree.rootId,id,deptName+"--"+userName+(isCreator=="1"?"(群主)":""),0,0,0,0,"");  
  104.                  if(userid==id)  
  105.                      friendTree.setItemColor(id,"red","");  
  106.              })      
  107.         })    
  108.     }  
  109.       
  110.      //---------------------------------------聊天室关键代码(websocket)---------------------------------------  
  111.      var stompClient=null;content=null;  
  112.      $(function(){  
  113.          connect();  
  114.      })  
  115.      //connect the server  
  116.      function connect(){  
  117.          var socket=new SockJS("/webchat");   
  118.          stompClient=Stomp.over(socket);  
  119.          stompClient.connect('','',function(frame){  
  120.              console.log('Connected: '+frame);  
  121.              //用户聊天订阅   
  122.              //alert("hello: "+frame);  
  123.              stompClient.subscribe("/userChat/chat"+roomid,function(chat){  
  124.                  showChat(JSON.parse(chat.body));  
  125.              });   
  126.                  
  127.              //初始化  
  128.              stompClient.subscribe("/app/initChat/"+roomid,function(initData){  
  129.                  //alert("初始化聊天室");      
  130.                  console.log(initData);      
  131.                  content=JSON.parse(initData.body);  
  132.                  //content=body.document.content;    
  133.                  //alert(content+":"+content.document.content);     
  134.                  content.forEach(function(item){  
  135.                      showChat(item);    
  136.                  });  
  137.                  sendMessage("1","进入");   
  138.              });  
  139.          },function(){   
  140.              connect();  
  141.          });   
  142.      }  
  143.        
  144.      //显示聊天信息  
  145.      function showChat(message){   
  146.           var htmlMsg=decodeURIComponent(message.chatContent);  
  147.             var image="<img src='/images/Pub/User.gif' width='16px' height='16px' align='absmiddle'/>";  
  148.             var userMsg=decodeURIComponent(message.deptName)  
  149.             +"--"+decodeURIComponent(message.userName)+"   "+decodeURIComponent(message.curTime)+"</font>";  
  150.             htmlMsg=userMsg+"<br/>    "+htmlMsg;    
  151.             if(htmlMsg!="") {  
  152.                 if($("#chatMsg").html()!=""){  
  153.                      if(message.isSysMsg=="1")    
  154.                          $("#chatMsg").append("<br/><div style='text-align:center'><font color='gray'>"+htmlMsg+"</div>");  
  155.                      else      
  156.                          $("#chatMsg").append("<br/>"+image+"<font color='blue'>"+htmlMsg);     
  157.                 }  
  158.                 else {  
  159.                      if(message.isSysMsg=="1")    
  160.                          $("#chatMsg").append("<div style='text-align:center'><font color='gray'>"+htmlMsg+"</div>");  
  161.                      else  
  162.                          $("#chatMsg").append(image+"<font color='blue'>"+htmlMsg);     
  163.                 }  
  164.                       
  165.                  $("#chatMsg")[0].scrollTop=$("#chatMsg")[0].scrollHeight;      
  166.             }   
  167.      }  
  168.        
  169.      function sendMessage(isSysMsg,textMsg){  
  170.          var chatCont=editor.getContent();  
  171.          if(isSysMsg=="1"){  
  172.              chatCont="<font color='gray'>"+textMsg+"聊天室</font>";  
  173.          }  
  174.          stompClient.send("/app/userChat",{},JSON.stringify({  
  175.              'roomid':encodeURIComponent(roomid),  
  176.              'userName':encodeURIComponent(username),  
  177.              'deptName':encodeURIComponent(deptSortName),  
  178.              'chatContent':encodeURIComponent(chatCont),     
  179.              'isSysMsg':encodeURIComponent(isSysMsg)  
  180.          }))  
  181.      }  
  182.      //---------------------------------------------------------------------------------------------------------------  
  183.           
  184. </script>    
  185.     </head>                       
  186.     <body style="width:100%;height:100%;margin:0px;overflow:hidden;">       
  187.         <div id="roomRemark"></div>     
  188.         <div  style="position:relative;width:99%;height:100%;overflow:auto;display:none;margin-left:5px;"  id="chatMsg"></div>  
  189.     </body>    
  190. </html>      
  191. </span>  
       五、实现效果

     创建人tester4进入后,输入聊天内容后,退出。


     聊天室好友tester1进入并发言


   文中代码部分参考了Spring WebSocket教程(一)Spring WebSocket教程(二)。


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值