zoukankan      html  css  js  c++  java
  • Websocket --(3)实现

    今天介绍另外一种websocket实现方式,结合了spring MVC,并完善了第二节所提到做一个简单的登录认证用来识别用户的名称。界面继续沿用第二节的布局样式,同时增加上线和下线功能。

    参考了 https://blog.csdn.net/mybook201314/article/details/70173674 这篇文章,但是同时做了部分改进。更多内容请看https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket  这是spring 官方文档,相当详细,我做的过程中更多的是看官方介绍。

    1.环境

    Eclisp + jdk1.7 +tomcat 8,低版本的Tomcat7可能跑不起来,项目采用maven构建。

    2.jar 包(只写websocket部分,spring 核心包就不写了)

            <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-websocket</artifactId>
               <version>4.0.2.RELEASE</version>
            </dependency>
    
            <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-messaging</artifactId>
               <version>4.0.2.RELEASE</version>
            </dependency>
            
            
        <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
           </dependency>
           
            <dependency>
                 <groupId>com.alibaba</groupId>
                 <artifactId>fastjson</artifactId>
                 <version>1.1.23</version>
           </dependency>

    这里添加了阿里巴巴的fastjson来处理json字符串。当然不用这个也可以。

    web.xml 配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xmlns="http://java.sun.com/xml/ns/javaee" 
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
             id="WebApp_ID" version="3.0">
        <absolute-ordering />
        <display-name>websocket2</display-name>
      <welcome-file-list>
        <welcome-file>login.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
      </welcome-file-list>
      
        <!-- Spring配置 -->
      <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-content.xml</param-value>
        </context-param>
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
      
    
        <!-- Spring MVC配置 -->
        <servlet>
            <servlet-name>spring</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml -->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:spring-mvc.xml</param-value>
            </init-param>
            <async-supported>true</async-supported>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>spring</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
        <!-- 中文过滤器 -->
        <filter>
            <filter-name>CharacterEncodingFilter</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>UTF-8</param-value>
            </init-param>
            <init-param>
                <param-name>forceEncoding</param-name>
                <param-value>true</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>CharacterEncodingFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
        
        <servlet-mapping>  
                <servlet-name>default</servlet-name>  
                <url-pattern>*.css</url-pattern>  
        </servlet-mapping>
        <servlet-mapping>  
                <servlet-name>default</servlet-name>  
                <url-pattern>*.gif</url-pattern>  
        </servlet-mapping>
        <servlet-mapping>  
                <servlet-name>default</servlet-name>  
                <url-pattern>*.jpg</url-pattern>  
        </servlet-mapping>  
        <servlet-mapping>  
                <servlet-name>default</servlet-name>  
                <url-pattern>*.js</url-pattern>  
        </servlet-mapping>  
        <servlet-mapping> 
                <servlet-name>default</servlet-name> 
                <url-pattern>*.html</url-pattern> 
        </servlet-mapping>
        
    </web-app>

    web.xml 里面有三点需要注意:命名空间要3.0及以上,添加配置文件开头添加<absolute-ordering />,具体可参看官方文档 Deployment 这个章节。

    构建完成的项目如下图所示

    没有用的文件请自行忽略

    3.实现

    首先新建一个自己的websocket处理类,这个类需要实现WebSocketHandler 这个接口,当然根据官方文档 里面的这句话Creating a WebSocket server is as simple as implementing WebSocketHandler or more likely extending either TextWebSocketHandler or BinaryWebSocketHandler 也可以实现TextWebSocketHandler或者BinaryWebSocketHandler。这个接口里面有几个方法分别对应连接前,连接成功后,和消息处理,关闭连接等操作。

    package com.lzl.ws;
    
    import java.util.ArrayList;
    import java.util.Map;
    
    import org.apache.log4j.Logger;
    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 com.alibaba.fastjson.JSON;
    
    //websocket处理类
    public class MyWebSocketHandler implements WebSocketHandler{
        private static final Logger log = Logger.getLogger(MyWebSocketHandler.class);
    
        // 保存所有的用户session
        private static final ArrayList<WebSocketSession> users = new ArrayList<WebSocketSession>();
    
        
        //连接关闭后
        @Override
        public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
                throws Exception {
            // TODO Auto-generated method stub
            log.info("已经关闭连接。。。。");
            users.remove(session);
        }
        
        //连接就绪后
        @Override
        public void afterConnectionEstablished(WebSocketSession session)
                throws Exception {
            log.info("已经成功连接。。。sessionID是"+session.getId());
            users.add(session);
            
        }
        
        //处理信息
        @Override
        public void handleMessage(WebSocketSession session, WebSocketMessage<?> message)
                throws Exception {
            log.info("收到消息。。。sessionID:"+session.getId());
            log.info("消息内容:"+message.getPayload().toString());
            Map<String,Object > map = JSON.parseObject(message.getPayload().toString(),Map.class);
            TextMessage textMessage = new TextMessage(map.get("userID").toString()+":"+map.get("msgContent").toString(),true);
            log.info("转发:"+textMessage.getPayload().toString());
            for(WebSocketSession user:users){
                user.sendMessage(textMessage);
            }
            log.info("发送完成。。。。");
        }
        
        //发生错误
        @Override
        public void handleTransportError(WebSocketSession session, Throwable throwable)
                throws Exception {
            // TODO Auto-generated method stub
            log.info("发生错误。。。。"+throwable.getMessage()+"*****SessionID"+session.getId());
        }
    
        @Override
        public boolean supportsPartialMessages() {
            // TODO Auto-generated method stub
            return false;
        }
    
    }

    我这里是把消息发送给所有已经连接的客户了

    接下来新建websocket注册类

    package com.lzl.ws;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.springframework.web.socket.config.annotation.EnableWebSocket;
    import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
    import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
    import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
    
    
    @Configuration
    @EnableWebMvc
    @EnableWebSocket
    public class MyWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{
    
        @Override
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            // TODO Auto-generated method stub
            registry.addHandler(new MyWebSocketHandler(), "/websocket").addInterceptors(new HttpSessionHandshakeInterceptor());
            
        }
    
    }

    这个类一定 要在spring MVC 自动扫描的包下面。其中 HttpSessionHandshakeInterceptor 类称为握手操作类,可以自己新建一个类去继承他,这里面可以根据需要写一些建立连接前,和建立连接后的动作,我这里暂时没有用到,所有就直接调用父类了。

    最后一步,在spring 配置文件中配置websocket 

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:context="http://www.springframework.org/schema/context"  
        xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:websocket="http://www.springframework.org/schema/websocket"
        xsi:schemaLocation="    
               http://www.springframework.org/schema/beans    
               http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
               http://www.springframework.org/schema/aop    
               http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
               http://www.springframework.org/schema/context    
               http://www.springframework.org/schema/context/spring-context-3.0.xsd
               http://www.springframework.org/schema/websocket
               http://www.springframework.org/schema/websocket/spring-websocket.xsd">
               
           
         <!-- 引入jdbc配置文件 -->    
       <!-- <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
            <property name="locations">  
                <list>  
                   <value>classpath:application.properties</value>  
                </list>  
            </property>  
        </bean>  --> 
        <context:property-placeholder location="classpath:application.properties"/> 
        <!-- 自动扫描注解的bean -->  
        <context:component-scan base-package="com.lzl" /> 
         
        <!-- 配置数据源 -->  
        <bean id="dataSource"  class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.user}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>  
      
        <!-- 配置Mybatis的文件 ,mapperLocations配置**Mapper.xml文件位置,configLocation配置mybatis-config文件位置-->  
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
            <property name="dataSource" ref="dataSource" />  
            <property name="mapperLocations" value="classpath:mapper/*.xml"/>    
            <property name="configLocation" value="classpath:mybatis-config.xml" />  
        </bean>  
      
        <!-- 自动扫描了所有的XxxxMapper.xml对应的mapper接口文件,这样就不用一个一个手动配置Mpper的映射了,只要Mapper接口类和Mapper映射文件对应起来就可以了。 -->  
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
            <property name="basePackage"   value="com.lzl.dao" /> 
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> 
        </bean>  
        
        
        <bean id = "Handler" class = "com.lzl.ws.MyWebSocketHandler"></bean>
        <websocket:handlers>
            <websocket:mapping path="/websocket" handler="Handler"/>
            <websocket:handshake-interceptors>
                <bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
            </websocket:handshake-interceptors>
        </websocket:handlers>
        
          
    </beans>  

    这里需要注意的是在在头文件添加webcoket支持。对于不支持的websocket的浏览器可以参照官方文档的  SockJS Fallback 这个章节处理,或者参考https://blog.csdn.net/mybook201314/article/details/70173674

    至此,websocket 相关内容完成,接着做登录操作。登录这里很简单,直接将用户名称和聊天页面的地址返回。

    package com.lzl.controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.servlet.ModelAndView;
    
    import com.lzl.dao.User;
    
    @Controller
    @RequestMapping("/login")
    public class LoginController {
        @RequestMapping(value = "/setUser.do",method=RequestMethod.POST)
        public ModelAndView login(User user ,HttpServletRequest request,HttpServletResponse response ){
            HttpSession session = request.getSession(); 
            session.setAttribute("user", user);
            ModelAndView mv = new ModelAndView();
            mv.addObject("user", user);
            mv.setViewName("chat");
            return mv;
        }
        
        @RequestMapping(value = "/getLogin.do",method=RequestMethod.GET)
        public ModelAndView getlogin(){
            System.out.println("-----------------------");
            ModelAndView mv = new ModelAndView();
            mv.setViewName("login");
            return mv;
        }
    }

    前端界面有两个,分别是登录界面和聊天界面,都是用bootstrap做的

    login.jsp

    <%@ page language="java" contentType="text/html; charset=utf-8"
        pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            <title>用户登录</title>
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta name="renderer" content="webkit">
    
            <script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
            <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
            <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
            <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
            <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
            <style type="text/css">
                body {
                    background-color:#f8f6e9;
                }
                .mycenter{
                    margin-top: 100px;
                    margin-left: auto;
                    margin-right: auto;
                    height: 350px;
                    width:500px;
                    padding: 5%;
                    padding-left: 5%;
                    padding-right: 5%;
                }
                .mycenter mysign{
                    width: 440px;
                }
                .mycenter input,checkbox,button{
                    margin-top:2%;
                    margin-left: 10%;
                    margin-right: 10%;
                }
                .mycheckbox{
                    margin-top:10px;
                    margin-left: 40px;
                    margin-bottom: 10px;
                    height: 10px;
                }
            </style>
            <script type="text/javascript">
                /* $(function(){
                    $("#submit").click(function(){
                        var data = $("#loginform").serialize();
                        $.post("${pageContext.request.contextPath}/login/setUser.do",data,function(result){
                            $("span").html(result);
                        });
                        
                    });
                    
                }); */
            
            
            
            
            </script>
            
    </head>
    <body>
    <form id="loginform" method = "post" action="${pageContext.request.contextPath}/login/setUser.do">
                <div class="mycenter">
                <div class="mysign">
                    <div class="col-lg-11 text-center text-info">
                        <h2>请登录</h2>
                    </div>
                    <div class="col-lg-10">
                        <input type="text" class="form-control" id = "username" name="username" placeholder="请输入账户名" required autofocus/>
                    </div>
                    <div class="col-lg-10"></div>
                    <div class="col-lg-10">
                        <input type="password" class="form-control" id = "password" name="password" placeholder="请输入密码" required autofocus/>
                    </div>
                    <div class="col-lg-10"></div>
                    <div class="col-lg-10 mycheckbox checkbox">
                        <input type="checkbox" class="col-lg-1">记住密码</input>
                    </div>
                    <div class="col-lg-10"></div>
                    <div class="col-lg-10">
                        <button type="submit" id = "submit" class="btn btn-success col-lg-12">登录</button>
                    </div>
                </div>
            </div>
            </form>
    </body>
    </html>

    chat.jsp

    <%@page import="com.lzl.dao.User"%>
    <%@ page language="java" contentType="text/html; charset=utf-8"
        pageEncoding="utf-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>聊天</title>
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta name="renderer" content="webkit">
    
            <script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
            <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
            <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
            <script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
            <script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
            <script type="text/javascript">
            var websocket;
            $(function() {
                // 首先判断是否 支持 WebSocket
                if('WebSocket' in window) {
                    websocket = new WebSocket("ws://localhost:8080/websocket2/websocket");
                } else if('MozWebSocket' in window) {
                    websocket = new MozWebSocket("ws://localhost:8080/websocket2/websocket");
                } else {
                    websocket = new SockJS("http://localhost:8080/websocket2/sockjs/websocket");
                }
                // 打开时
                websocket.onopen = function(evnt) {
                    $("#tou").html("链接服务器成功!")
                };
                // 处理消息时
                websocket.onmessage = function(evnt) {
                    console.log(evnt.data);
                    $("#msg").html($("#msg").html() + "<br/>" + evnt.data);
                };
                websocket.onerror = function(evnt) {
                    console.log("  websocket.onerror  ");
                };
                websocket.onclose = function(evnt) {
                    $("#tou").html("与服务器断开了链接!")
                };
                // 点击了发送消息按钮的响应事件
                $("#send").click(function(){
                    // 获取消息内容
                    var text = $("#message").val();
                    // 判断
                    if(text == null || text == ""){
                        alert(" content  can not empty!!");
                        return false;
                    }
                    var username = $("#username").val();
                    console.log("username="+username);
                    var msg = {
                        msgContent: text,
                        userID:username
                    };
                    // 发送消息
                    websocket.send(JSON.stringify(msg));
                    $("#message").val("");
                });
                
                $("#offline").click(function(){
                    console.log(websocket.readyState);
                    var state = websocket.readyState;
                    if(1==state){
                        websocket.close();
                        $("#offline").html("上线");
                    }else{
                        connect();
                        $("#offline").html("下线");
                    }
                });
                
                
            });
            
            
            function connect (){
                // 首先判断是否 支持 WebSocket
                if('WebSocket' in window) {
                    websocket = new WebSocket("ws://localhost:8080/websocket2/websocket");
                } else if('MozWebSocket' in window) {
                    websocket = new MozWebSocket("ws://localhost:8080/websocket2/websocket");
                } else {
                    websocket = new SockJS("http://localhost:8080/websocket2/sockjs/websocket");
                }
                // 打开时
                websocket.onopen = function(evnt) {
                    $("#tou").html("链接服务器成功!")
                };
                // 处理消息时
                websocket.onmessage = function(evnt) {
                    $("#msg").html($("#msg").html() + "<br/>" + evnt.data);
                };
                websocket.onerror = function(evnt) {
                    console.log("  websocket.onerror  ");
                };
                websocket.onclose = function(evnt) {
                    $("#tou").html("与服务器断开了链接!")
                };
            }
            
            //给发送按钮绑定回车键事件
            $(document).keydown(function(event){ 
                if(event.keyCode == 13){ //绑定回车 
                $('#send').click(); 
            } 
            }); 
            </script>
    </head>
    <body>
        <input type="hidden" value="${user.username}" id="username">
        <div>
        <div class="page-header" id="tou" style = "text-align:center">
                webSocket多终端聊天测试
            </div>
            <div class="well" id="msg" style = "800px;margin:0 auto"></div>
            &nbsp;
            <div class="col-lg">
                <div class="input-group" style = "800px;margin:0 auto">
                    <input type="text" class="form-control" placeholder="发送信息..." id="message">
                    <span class="input-group-btn">
                        <button class="btn btn-default" type="button" id="send" >发送</button>
                    </span>
                </div>
            </div>
     </div>
     &nbsp;
     <div class="container-fluid">
        <div class="row-fluid">
            <div class="span12">
                 <button class="btn btn-warning btn-block" type="button" id= "offline" style = "800px;margin:0 auto">下线</button>
            </div>
        </div>
    </div>
    </body>
    </html>

    4.效果

    这是admin这个用户的界面

    这是王小二的界面

  • 相关阅读:
    Aspnet_regsql.exe命令行使用小结
    ListView用法学习
    < %=...%>< %#... %>< % %>< %@ %>
    jQuery入门简介
    oracle基础琐碎总结删除数据
    WindowsPhone基础琐碎总结数据绑定(一)
    ADO.NET基础琐碎总结参数化查询
    oracle基础琐碎总结Where和Having的区别与联系
    第一次使用 Windows Live Writer
    WindowsPhone基础琐碎总结数据绑定(二)
  • 原文地址:https://www.cnblogs.com/li-zhi-long/p/9366935.html
Copyright © 2011-2022 走看看