初识webSocket

昨天公司派给我一个任务 让我跟后端联调webSocket。之前没接触过这个东西,就记录下来

用通俗的话来讲的话 传统HTTP协议 如果请求数据或者信息,只能客户端发送数据给服务端。然后服务端接收数据并响应给客户端,说白了就是有缺陷。只能客户端主动。服务端是被动的。然而webSocket出现了。webSocket的出现从而解决了这一现象。不仅客户端主动,服务端也能主动。说白了讲webSocket就是客户端与服务端一种双向的通讯协议。不仅优化传送数据的带宽。而且解决了传统出现的缺陷这一难题。下面放代码

前端代码

<!DOCTYPE HTML>
<html>

<head>
    <title>大屏B</title>
</head>

<body>
    Welcome<br/>
    <input id="text" type="text" /><button onclick="send()">Send</button> <button onclick="closeWebSocket()">Close</button>
    <div id="message">
    </div>
</body>

<script type="text/javascript">
    var websocket = null;
    var wsGroupId = "20191009170924631538723061895168";// 部门id,区别分组
    var type = "screenB"// 相当于标识,区分屏幕,同一个分组从A-Z代表26个屏幕
    var url = "ws://192.168.50.174:8011/ws/" + wsGroupId + "/" + type;
    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket(url);
    } else {
        alert('Not support websocket')
    }

    //连接发生错误的回调方法
    websocket.onerror = function() {
        // setMessageInnerHTML("error");
        console.log(getNowTime() + ' 发生异常了');
        reconnect(url);
    };

    //连接成功建立的回调方法
    websocket.onopen = function(event) {
        //setMessageInnerHTML("open");
        console.log(getNowTime() + " Socket 已打开");
        send("heartbeat");
        //心跳检测重置
        heartCheck.start()
    }

    //接收到消息的回调方法
    websocket.onmessage = function(event) {
        //websocket.setMessageInnerHTML(event.data);
        // 维持心跳
        heartCheck.start();
        if (event.data != 'heartbeat') {
            document.getElementById("text").value = event.data;
            console.log(event.data)
        }
        //window.location.href="http://www.jb51.net";
    }

    //连接关闭的回调方法
    websocket.onclose = function() {
        // websocket.setMessageInnerHTML("close");
        console.log(getNowTime() + " Socket已关闭");
        reconnect(url)
    }

    //    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function() {
        websocket.close();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }

    //关闭连接
    function closeWebSocket() {
        websocket.close();
        lockReconnect = false;
        reconnect(url);
    }

    //发送消息
    function send(message) {
        // var message = document.getElementById('text').value;
        websocket.send(message);
    }

    var lockReconnect = false; //避免重复连接
    //重试连接socket
    function reconnect(url) {
        if (lockReconnect) {
            return;
        };
        lockReconnect = true;
        //没连接上会一直重连,设置延迟避免请求过多
        tt && clearTimeout(tt);
        tt = setTimeout(function() {
            createWebSocket(url);
            lockReconnect = false;
        }, 10000);
    }
    //心跳检测
    var heartCheck = {
        timeout: 5000,
        timeoutObj: null,
        serverTimeoutObj: null,
        start: function() {
            console.log(getNowTime() + " Socket 心跳检测");
            var self = this;
            this.timeoutObj && clearTimeout(this.timeoutObj);
            this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
            this.timeoutObj = setTimeout(function() {
                //这里发送一个心跳,后端收到后,返回一个心跳消息,
                //onmessage拿到返回的心跳就说明连接正常
                console.log(getNowTime() + ' Socket 连接重试');
                send("heartbeat");
                self.serverTimeoutObj = setTimeout(function() {
                    console.log(this);
                    close();
                }, self.timeout);
            }, this.timeout)
        }
    }

    /**
     * 获取系统当前时间
     * @returns
     */
    function p(s) {
        return s < 10 ? '0' + s : s;
    }

    function getNowTime() {
        var myDate = new Date();
        //获取当前年
        var year = myDate.getFullYear();
        //获取当前月
        var month = myDate.getMonth() + 1;
        //获取当前日
        var date = myDate.getDate();
        var h = myDate.getHours(); //获取当前小时数(0-23)
        var m = myDate.getMinutes(); //获取当前分钟数(0-59)
        var s = myDate.getSeconds();
        return year + '-' + p(month) + "-" + p(date) + " " + p(h) + ':' + p(m) + ":" + p(s);
    }
</script>

</html>

付java代码

 package com.example.springtest01.socket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 * @author feiyang
 */
@ServerEndpoint(value = "/ws/{wsGroupId}/{type}")
@Component
public class WebSocket {
    private static final Logger logger = LoggerFactory.getLogger(WebSocket.class);
    private static SessionStorage sessionStorage = SessionStorage.getInstance();
    private static Map<String, String> sessionGroup = new ConcurrentHashMap<>(16);
    /**
     * 连接建立成功调用的方法
     * @param session
     * @param type
     * @param wsGroupId
     * @throws Exception
     */
    @OnOpen
    public void onOpen(@PathParam("wsGroupId") String wsGroupId, @PathParam("type") String type, Session session) {
        sessionGroup.put(session.getId(), wsGroupId);
        sessionStorage.putSession(session, wsGroupId, type);
        session.setMaxIdleTimeout(3600000);
        logger.info("websocket连接成功 sessionId:" + session.getId());
        logger.info("websocket连接成功 type:" + type);
        logger.info("websocket连接成功 wsGroupId:" + wsGroupId);
        logger.info("websocket链接数量:" + sessionGroup.size());
    }
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("wsGroupId") String wsGroupId, @PathParam("type") String type, Session session) {
        logger.info("websocket连接关闭成功 sessionId:" + session.getId());
        logger.info(type);
        logger.info(sessionGroup.get(session.getId()));
        SessionStorage.getInstance().removeSession(sessionGroup.get(session.getId()), type);
    }
    /**
     * 收到客户端消息后调用的方法
     * @param message
     * @param session
     */
    @OnMessage
    public void onMessage(@PathParam("wsGroupId") String wsGroupId, @PathParam("type") String type,String message, Session session) {
        try {
            logger.info(message);
            sessionStorage.sendTextSingle( wsGroupId,  type,  message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 发生错误时调用
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.info("websocket连接失败 sessionId:" + session.getId());
    }
}
package com.example.springtest01.socket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.util.StringUtils;
import javax.websocket.Session;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 *
 * 作为对外的工具类
 * @author zhoukx
 */
public class SessionStorage {
    private Map<String, Map<String, Session>> sessionStore = new ConcurrentHashMap<>();
    private static volatile SessionStorage storage;
    private static final Logger logger = LoggerFactory.getLogger(SessionStorage.class);
    private static final String  FLAG_HEART_BEAT = "heartbeat";
    private SessionStorage() {
    }
    /**
     *   双端检索机制创建一个   SessionStorage 实例
     * @return
     */
    public static synchronized SessionStorage getInstance() {
        if(storage == null) {
            synchronized(SessionStorage.class) {
                if(storage == null) {
                    storage = new SessionStorage();
                }
            }
        }
        return storage;
    }
    /**
     * 将ws连接会话分组保存
     * @author  feiyang
     * @param session
     * @param wsGroupId
     * @param type
     * @return  void
     * @date    2019/9/19
     * @throws
     */
    public void putSession(Session session, String wsGroupId, String type) {
        if(session != null) {
            Map<String, Session> sessionGroup = this.sessionStore.get(wsGroupId);
            if (sessionGroup == null) {
               sessionGroup = new ConcurrentHashMap<>(16);
               this.sessionStore.put(wsGroupId, sessionGroup);
            }
            sessionGroup.put(type, session);
        }
    }
    /**
     * ws连接关闭时删除会话
     * @author  feiyang
     * @param wsGroupId
     * @param type
     * @return  void
     * @date    2019/9/19
     * @throws
     */
    public void removeSession(String wsGroupId, String type) {
        Map<String, Session> sessionGroup = sessionStore.get(wsGroupId);
        sessionGroup.remove(type);
        if (sessionGroup.isEmpty()) {
            this.sessionStore.remove(wsGroupId);
        }
    }
    /**
     * 获取指定ws连接会话
     * @author  feiyang
     * @param wsGroupId
     * @param type
     * @return  javax.websocket.Session
     * @date    2019/9/19
     * @throws
     */
    public Session getSession(String wsGroupId, String type) {
        Map<String, Session> sessionGroup = this.sessionStore.get(wsGroupId);
        return sessionGroup.get(type);
    }
    /**
     * 获取指定ws连接会话组全部session
     * @author  feiyang
     * @param wsGroupId
     * @return  java.util.List<javax.websocket.Session>
     * @date    2019/9/19
     * @throws
     */
    public List<Session> getSessions(String wsGroupId) {
        Session[] sessions = new Session[]{};
        Map<String, Session> sessionGroup = this.sessionStore.get(wsGroupId);
        return Arrays.asList(sessionGroup.values().toArray(sessions));
    }
    /**
     * 获取全部的会话
     * @author  feiyang
     * @param
     * @return  java.util.List<javax.websocket.Session>
     * @date    2019/9/19
     * @throws
     */
    public List<Session> getAllSession() {
        List<Session> sessionList = new ArrayList<>();
        for (Map<String, Session> sessionGroup : sessionStore.values()) {
            Session[] sessions = new Session[]{};
            sessionList.addAll(Arrays.asList(sessionGroup.values().toArray(sessions)));
        }
        return sessionList;
    }
    /**
     * 针对单个会话发送文本消息
     * @author  feiyang
     * @param wsGroupId
     * @param type
     * @param text
     * @return  void
     * @date    2019/9/19
     * @throws
     */
    @Async
    public void sendTextSingle(String wsGroupId, String type, String text){
        try {
            Session session = getSession(wsGroupId, type);
            if (session.isOpen()) {
                session.getBasicRemote().sendText(text);
                if (text.equals(FLAG_HEART_BEAT)) {
                    logger.info("socket维持链接状态:" + text);
                } else {
                    logger.info("已发送》》》》》" + text);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 针对会话组发送文本信息
     * @author  feiyang
     * @param wsGroupId
     * @param text
     * @return  void
     * @date    2019/9/19
     * @throws
     */
    @Async
    public void  sendTextGroup(String wsGroupId, String text) {
        try {
            List<Session> sessions = getSessions(wsGroupId);
                for(Session session : sessions) {
                    if (session.isOpen()) {
                        session.getBasicRemote().sendText(text);
                        logger.info("已发送》》》》》" + text);
                    }
                }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//    /**
//     * 针对全部会话发送消息
//     * @author  feiyang
//     * @param text
//     * @return  void
//     * @date    2019/9/19
//     * @throws
//     */
//    public void sendTextAll(String text) {
//        try {
//            List<Session> sessions = getAllSession();
//            for(Session session : sessions) {
//                if (session.isOpen()) {
//                    session.getBasicRemote().sendText(text);
//                    logger.info("已发送》》》》》" + text);
//                }
//            }
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }
}

package com.example.springtest01.socket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
 * @Author :feiyang
 * @Date :Created in 4:03 PM 2019/9/18
 */
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter (){
        return new ServerEndpointExporter();
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值