zoukankan      html  css  js  c++  java
  • tomcat websocket 实现网页在线即时聊天

    背景介绍

           近一个月完成了公司的一个项目,负责即时聊天部分

           寻找了一下,决定使用websocket,要问原因的话,因为tomcat 自带相关消息收发的API,用起来方便

    闲话少叙,进入实现步骤

          使用工具  java 1.6      tomcat 7.0.27以上版本(以下版本不支持websocket),本人使用的是 7.0.42版本

          先概括说一下:(看着迷糊没关系,下面有提供完整源码,可以下载后运行 ,结合效果自行分析)

          先写一个,消息处理类,继承 tomcat的catalina.jar的MessageInbound类,并创建一个构造方法获取登录名

          继承onPen,onClose等方法对用户上线下线进行监听,重写onTextMessage(CharBuffer msg)方法,

          实现消息的处理,接收和发送

          再写一个网页应用必须的servlet类接收界面请求,继承WebSocketServlet类,实现登陆监控

          既然是即时聊天,则需要知道在线用户有哪些,则需要一个在线用户容器类

          另外,消息send(),参数只有一个,所以里面会包含消息发送者和消息接收者,需要解析,所以写了一个解析类

         那么开始详细代码解释

        

    package socket;
    
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.CharBuffer;
    import java.util.HashMap;
    
    import org.apache.catalina.websocket.MessageInbound;
    import org.apache.catalina.websocket.WsOutbound;
    
    import util.MessageUtil;
    
    public class MyMessageInbound extends MessageInbound {
        // 用户姓名 
        private String name;
        
        public MyMessageInbound() {
            super();
        }
    
        public MyMessageInbound(String name) {
            super();
            this.name = name;
        }
    
        @Override  
        protected void onBinaryMessage(ByteBuffer arg0) throws IOException {  
    
        }  
    
        @Override  
        protected void onTextMessage(CharBuffer msg) throws IOException { 
            //  用户所发消息处理后的map
            HashMap<String,String> messageMap = MessageUtil.getMessage(msg);    //处理消息类
            //  在线用户集合类map
            HashMap<String, MessageInbound> userMsgMap = InitServlet.getSocketMap();
            
            String fromName = messageMap.get("fromName");   // 消息来源
    
    
            String toName = messageMap.get("toName");       // 消息接收人
            //获取该用户
            MessageInbound messageInbound = userMsgMap.get(toName);    //在容器中获得接收人的MessageInbound
            
            MessageInbound messageFromInbound = userMsgMap.get(fromName);   //在容器中获得发送人的MessageInbound
    
            if(messageInbound!=null && messageFromInbound!=null){     // 如果发往人 存在进行操作
                WsOutbound outbound = messageInbound.getWsOutbound(); 
    
                WsOutbound outFromBound = messageFromInbound.getWsOutbound();
                
                String content = messageMap.get("content");  // 消息内容
                String msgContentString = fromName + "说: " + content;   // 构造消息体
    
                //发出去内容
                CharBuffer toMsg =  CharBuffer.wrap(msgContentString.toCharArray());
                
                CharBuffer fromMsg =  CharBuffer.wrap(msgContentString.toCharArray());
                // 消息发送到前端
                outFromBound.writeTextMessage(fromMsg);
                outbound.writeTextMessage(toMsg);  //
                outFromBound.flush();
                outbound.flush();
            }
    
        }  
    
        @Override  
        protected void onClose(int status) {  
            // 用户离线后 在线用户容器 去除该用户
            InitServlet.getSocketMap().remove(this);  
            super.onClose(status);  
        }  
    
        @Override
        protected void onOpen(WsOutbound outbound) {  
            super.onOpen(outbound);  
            //
            if(name!=null){
                // 用户登录后,存入在线用户容器
                InitServlet.getSocketMap().put(name, this);
            }
        }
    
        @Override
        public int getReadTimeout() {
            return 0;
        }  
    
    
    }

    然后是 请求接收类

    package socket;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.catalina.websocket.StreamInbound;
    import org.apache.catalina.websocket.WebSocketServlet;
    public class MyWebSocketServlet extends WebSocketServlet {
    
        /**
         * 
         */
        private static final long serialVersionUID = -6488889268352650321L;
        
        
        public String getUser(HttpServletRequest request){
            String userName = (String) request.getSession().getAttribute("user");
            if(userName==null){
                return null;
            }
            return userName;  
        }  
        
        @Override
        protected StreamInbound createWebSocketInbound(String arg0,
                HttpServletRequest request) {
            System.out.println("用户" + request.getSession().getAttribute("user") + "登录");
            return new MyMessageInbound(this.getUser(request)); 
        }
    }

    在线用户容器初始化类

    package socket;
    
    import java.util.HashMap;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    
    import org.apache.catalina.websocket.MessageInbound;
    
    /**
     * 应用启动时加载此类   
     * 初始化servlet
     * 在应用启动后  在线用户的容器就准备好了!
     * @author masan
     *
     */
    public class InitServlet extends HttpServlet {
    
    
        private static final long serialVersionUID = 5772968684237694231L;
        
        // 在线用户容器  (存储在线用户   键值对   name:MessageInbound)
        private static HashMap<String,MessageInbound> socketMap;    
          
        public void init(ServletConfig config) throws ServletException {    
            InitServlet.socketMap = new HashMap<String,MessageInbound>();    
            super.init(config);    
            System.out.println("Server Started!");    
        }    
            
        /**
         * 获取在线用户容器的方法
         * @return
         */
        public static HashMap<String,MessageInbound> getSocketMap() {    
            return InitServlet.socketMap;    
        }    
    }

    然后是 消息体的解析类,我是按照逗号分隔的   前两处分别为发送者和接收者姓名,后一处为消息内容

    package util;
    
    import java.nio.CharBuffer;
    import java.util.HashMap;
    public class MessageUtil {
        public static HashMap<String,String> getMessage(CharBuffer msg) {
            HashMap<String,String> map = new HashMap<String,String>();
            String msgString  = msg.toString();
            String m[] = msgString.split(",");
            map.put("fromName", m[0]);
            map.put("toName", m[1]);
            map.put("content", m[2]);
            return map;
        }
    }

    那么附上工程目录结构:

    附上三个JSP的内容

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Index</title>
    <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
    <%session.setAttribute("user", "小化");%>
    <script type="text/javascript">
    var ws = null;
    function startWebSocket() {
        if ('WebSocket' in window)
            ws = new WebSocket("ws://localhost:8080/WebSocketUser/websocket.do");
        else if ('MozWebSocket' in window)
            ws = new MozWebSocket("ws://localhost:8080/WebSocketUser/websocket.do");
        else
            alert("not support");
        
        
        ws.onmessage = function(evt) {
    //         alert(evt.data);
            console.log(evt);
           // $("#xiaoxi").val(evt.data);
            setMessageInnerHTML(evt.data);
        };
        function setMessageInnerHTML(innerHTML){
            document.getElementById('message').innerHTML += innerHTML + '<br/>';
        }
        ws.onclose = function(evt) {
            //alert("close");
            document.getElementById('denglu').innerHTML="离线";
        };
        
        ws.onopen = function(evt) {
            //alert("open");
            document.getElementById('denglu').innerHTML="在线";
            document.getElementById('userName').innerHTML='小化';
        };
    }
    
    function sendMsg() {
        var fromName = "小化";
        var toName = document.getElementById('name').value;  //发给谁
        var content = document.getElementById('writeMsg').value; //发送内容
        ws.send(fromName+","+toName+","+content);
    }
    </script>
    </head>
    <body onload="startWebSocket();">
    <p>聊天功能实现</p>
    登录状态:
    <span id="denglu" style="color:red;">正在登录</span>
    <br>
    登录人:
    <span id="userName"></span>
    <br>
    <br>
    <br>
    
    发送给谁:<input type="text" id="name" value="小明"></input>
    <br>
    发送内容:<input type="text" id="writeMsg"></input>
    <br>
    聊天框:<div id="message" style="height: 250px; 280px;border: 1px solid; overflow: auto;"></div>
    <br>
    <input type="button" value="send" onclick="sendMsg()"></input>
    </body>
    </html>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Index</title>
    <script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
    <%session.setAttribute("user", "小明");%>
    <script type="text/javascript">
    var ws = null;
    function startWebSocket() {
        if ('WebSocket' in window)
            ws = new WebSocket("ws://localhost:8080/WebSocketUser/websocket.do");
        else if ('MozWebSocket' in window)
            ws = new MozWebSocket("ws://localhost:8080/WebSocketUser/websocket.do");
        else
            alert("not support");
        
        
        ws.onmessage = function(evt) {
            console.log(evt);
            //alert(evt.data);
            //$("#xiaoxi").val(evt.data);
            setMessageInnerHTML(evt.data);
        };
        function setMessageInnerHTML(innerHTML){
            document.getElementById('message').innerHTML += innerHTML + '<br/>';
        }
        ws.onclose = function(evt) {
            //alert("close");
            document.getElementById('denglu').innerHTML="离线";
        };
        
        ws.onopen = function(evt) {
            //alert("open");
            document.getElementById('denglu').innerHTML="在线";
            document.getElementById('userName').innerHTML="小明";
        };
    }
    
    function sendMsg() {
        var fromName = "小明";
        var toName = document.getElementById('name').value;  //发给谁
        var content = document.getElementById('writeMsg').value; //发送内容
        ws.send(fromName+","+toName+","+content);
    }
    </script>
    </head>
    <body onload="startWebSocket();">
    <p>聊天功能实现</p>
    登录状态:
    <span id="denglu" style="color:red;">正在登录</span>
    <br>
    登录人:
    <span id="userName"></span>
    <br>
    <br>
    <br>
    
    发送给谁:<input type="text" id="name" value="小化"></input>
    <br>
    发送内容:<input type="text" id="writeMsg"></input>
    <br>
    聊天框:<div id="message" style="height: 250px; 280px;border: 1px solid; overflow: auto;"></div>
    <br>
    <input type="button" value="send" onclick="sendMsg()"></input>
    </body>
    </html>
    

      附上两个JSP,可自行增加,

    另:附上web.xml  ,相当重要,里面进行了servlet配置,并且设置了要求

      initServlet在应用启动时就要执行
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    
    	<servlet>
    		<servlet-name>websocket</servlet-name>
    		<servlet-class>socket.MyWebSocketServlet</servlet-class>
    	</servlet>
    	<servlet-mapping>
    		<servlet-name>websocket</servlet-name>
    		<url-pattern>*.do</url-pattern>
    	</servlet-mapping>
    
    	<servlet>
    		<servlet-name>initServlet</servlet-name>
    		<servlet-class>socket.InitServlet</servlet-class>
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    	<welcome-file-list>
    		<welcome-file>index.jsp</welcome-file>
    	</welcome-file-list>
    </web-app>
    

      

    注意事项:1.js代码内的websocket 构造参数里的IP   要和最终浏览器访问地址一致  要么都是localhost要么都是IP  否则跨域

                        

                            2.这个demo内是根据用户名识别用户的,明显不能做到唯一,所以只能是个demo,要用到项目里,请考虑下然后稍作修改即可

                            (我生产项目里用的mysql数据库,最后使用的用户ID来做区别)

                           

                            3.消息发送参数只有一个,那么发送者和接收者的信息一并发送,后台有个解析类,如果需要传递其他参数,可自行修改解析类的解析方案

                             

                  完整代码下载地址

             想免积分下载的,但是CSDN最新修改,没有零分的了,最低1分

                    http://download.csdn.net/download/u012580998/9941412

                   增加百度云盘下载地址

                    https://pan.baidu.com/s/1o7W6yW6

                        

  • 相关阅读:
    RUST实践.md
    redis.md
    opencvrust.md
    aws rds can't connect to mysql server on 'xx'
    Foundation ActionScript 3.0 With Flash CS3 And Flex
    Foundation Flash Applications for Mobile Devices
    Flash Mobile Developing Android and iOS Applications
    Flash Game Development by Example
    Actionscript 3.0 迁移指南
    在SWT中非UI线程控制界面
  • 原文地址:https://www.cnblogs.com/maxm/p/7397180.html
Copyright © 2011-2022 走看看