• WebSocket浅析(一):实现群聊功能


    首先WebSocket打破了传统的web请求响应模式,实现管道式的实时通信,并且可以持续连接。

    相对于传统 HTTP 每次请求-应答都需要客户端与服务端建立连接的模式,WebSocket 是类似 Socket 的 TCP 长连接的通讯模式,一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。在客户端断开 WebSocket 连接或 Server 端断掉连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。

    所需jar包:websocket-api.jar,tomcat从7.0版本开始支持WebSocket,并且已经包含了所需jar包

    websocket在tomcat中的参考配置文件路径:

    webappsexamplesWEB-INFclasseswebsocket

    webappsexampleswebsocket

    首先我们需要一个class去实现ServerApplicationConfig接口作为配置文件,此时必须重写两个方法,分别是getEndpointConfigs()、getAnnotatedEndpointClasses(),getEndPointConfigs 获取所有以接口方式配置的webSocket类,getAnnotatedEndpointClasses 扫描src下所有用@ServerEndPoint注解的类。通常我们使用注解扫描的方法去实现,毕竟方便吧,该方法需要传入一个一class作为元素的Set集合,该集合表示扫描到的类,并且返回这个Set集合

    package com.asen.websocket.config;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.websocket.Endpoint;
    import javax.websocket.server.ServerApplicationConfig;
    import javax.websocket.server.ServerEndpointConfig;
    
    public class WebSocketConfig implements ServerApplicationConfig{
    
        @Override
        public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
            
            Set<Class<?>> results = new HashSet<Class<?>>();
            
            for (Class<?> clazz : scanned) {
                if (clazz.getPackage().getName().startsWith("com.asen.websocket.")) {
                    results.add(clazz);
                }
            }
            
            return results;
        }
    
        @Override
        public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> arg0) {
            // TODO Auto-generated method stub
            return null;
        }
    
    }

     

    此时我们需要一个类并添加@ServerEndpoint("/chat")注解,此时表示的请求路径为ws://IP地址:8080/工程名称/chat,注意这里用的是ws协议,并不是传统的http,在这里类中通常用到三个注解:@OnOpen这个注解下的类会在客户端发起请求的时候执行,@OnMessage注解下的类通常是在接收客户端发送数据到服务器的时候执行,@OnClose注解下的类通常是在客户端断开连接的时候执行。

    常用的Api:

    获取服务器发送的请求参数session.getQueryString();

    向客户端发送数据session.getBasicRemote().sendText(msg);

    package com.asen.websocket.service;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
    
    import com.asen.websocket.entity.User;
    import com.google.gson.Gson;
    
    @ServerEndpoint("/chat")
    public class SocketService {
        
        private Session session;
        private static String username;
        private static Set<SocketService> connections = new HashSet<SocketService>();
        private static List<String> usernames = new ArrayList<String>();
        
        /*
         * websocket并不是单例的,每增加一名用户使用系统,就会创建一个websocket实例,
         * 所以session这个全局变量有多份,不能用static修饰,
         * 而广播消息的时候是根据session来判断发给谁的------------s.session.getBasicRemote().sendText(msg);
         * 如果session用static修饰,那么消息只会发送给同一个人,并且当前系统有多少个用户,就会收到多少份相同的消息
         */
        public SocketService(){
            System.out.println("创建了SocketServcie()实例");
        }
    
        @OnOpen
        public void connected(Session session) {
            this.session = session;
            connections.add(this);
            
            //获取服务器发送的请求参数
            String str = session.getQueryString();
            
            this.username = str.substring(str.indexOf("=") + 1);
            
            System.out.println(username + "开始使用本系统" + ", " + "sessionId为:" + session.getId());
            
            usernames.add(username);
            
            User user = new User();
            
            user.setNames(usernames);
            
            Gson gson = new Gson();
            
            broadcast(connections, gson.toJson(user));
        }
        
        /*
         *服务器通过参数msg接收客户端发送过来的数据
         */
        @OnMessage
        public void getMsg(Session session, String msg) {
            
            User user = new User();
            user.setDate(new Date().toLocaleString());
            user.setFrom(this.username);
            user.setSendMsg(msg);
            
            Gson gson = new Gson();
            
            broadcast(connections, gson.toJson(user));
        }
        
        @OnClose
        public void close(Session session){
            connections.remove(this);
            usernames.remove(this.username);
            
            User user = new User();
            
            user.setNames(usernames);
            
            Gson gson = new Gson();
            
            broadcast(connections, gson.toJson(user));
        }
        
        //广播消息
        public static void broadcast(Set<SocketService> sockets, String msg){
            for (SocketService s : sockets){
                try {
                    //向客户端发送数据
                    s.session.getBasicRemote().sendText(msg);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
  • 相关阅读:
    'jar' 不是内部或外部命令,也不是可运行的程序 或批处理文件。或批处理文件
    idea 集成 javap
    JVM_ 动态链接
    远程服务调用RMI框架 演示,和底层原理解析
    IO/NIO/AIO 解读
    JUC 并发编程--10, 阻塞队列之--LinkedBlockingDeque 工作窃取, 代码演示
    github,使用技巧, 让你的开发事半功倍
    JVM-相关,这里引用别人博客
    python开发学习day01 (编程; 计算机三大核心硬件 ; 操作系统与平台)
    webdriervAPI(多表单切换)
  • 原文地址:https://www.cnblogs.com/asen0713/p/6456055.html
Copyright © 2020-2023  润新知