websocket学习

传统http协议

传统http协议是无状态的,每次请求都要由客户端主动发起,服务端进行处理后返回结果,而服务端很难主动主动向客户端发送消息
对于即时通信等功能来说使用http有很大的不便之处。
    轮询:是最原始的实现实时web应用的解决方案。轮询技术要求客户端以设定的时间间隔周期性地向服务端发送请求,
频繁地查询是否有新的数据改动。这样方法会导致过多不必要的请求,浪费流量和资源
    Coment技术:又可以分为长轮询和流技术。长轮询改进了上述的轮询技术,减小了无用的请求。它为某些数据设置了过期时间,
当数据过期后才会向服务端发送请求。流技术通常指客户端使用一个隐藏的窗口与服务端建立一个HTTP长连接,服务端会不断更新
连接状态以保持HTTP长连接存活
	这两种技术都是基于请求-应答模式,都不算是真正意义上的实时技术;他们的每次请求,应答都狼浪费流量在头信息上,复杂

WebSocket(伴随HTML5推出)

websocket是一个持久化的,在单个TCP连接上进行全双工通讯的协议。
web浏览器和服务器都必须实现websocket协议来建立和维护连接,websocket连接长期存在,与典型的http连接不同,对服务器有重要影响
   基于多线程或多进程的服务器无法适用于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets
服务器端实现都需要一个异步服务器

简单websocket实现

@ServerEndpoint(注册websocket服务注解)
要实现的四个注解(四个方法)
@OnOpen(连接建立成功调用的方法)
@OnClose(连接关闭调用的方法)
@OnMessage(收到客户端消息调用的方法)
@OnError(连接错误调用的方法)

后台代码如下:
	@ServerEndpoint("/websocket/{userId}")
	@Component
	public class WebSocketServer {
	
	    static Logger log = Logger.getLogger(WebSocketServer.class);
	    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
	    private static int onlineCount = 0;
	    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
	    //private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
	
	    //新:使用map对象,便于根据userId来获取对应的WebSocket
	    private static ConcurrentHashMap<String, WebSocketServer> websocketList = new ConcurrentHashMap<>();
	
	    //与某个客户端的连接会话,需要通过它来给客户端发送数据
	    private Session session;
	
	    //接收userId
	    private String userId = "";
	
	    /**
	     * 连接建立成功调用的方法
	     */
	    @OnOpen
	    public void onOpen(Session session, @PathParam("userId") String userId) {
	        this.session = session;
	        websocketList.put(userId, this);
	        log.info("websocketList->" + JSON.toJSONString(websocketList));
	        addOnlineCount();           //在线数加1
	        log.info("有新窗口开始监听:" + userId + ",当前在线人数为" + getOnlineCount());
	        this.userId = userId;
	        try {
	            Enumeration<String> keys = websocketList.keys();
	            if(keys.hasMoreElements()){
	                System.out.println(keys.nextElement());
	            }
	            MessageFormat messageFormat = new MessageFormat();
	            messageFormat.setFromUserId("系统消息");
	            messageFormat.setReceiveMessage("用户-" + userId + "-连接成功");
	            messageFormat.setMessageType("1");
	            sendMessage(JSON.toJSONString(messageFormat));
	        } catch (IOException e) {
	            log.error("websocket IO异常");
	        }
	    }
	
	    /**
	     * 连接关闭调用的方法
	     */
	    @OnClose
	    public void onClose() {
	        if (websocketList.get(this.userId) != null) {
	            websocketList.remove(this.userId);
	            subOnlineCount();           //在线数减1
	            log.info("用户:" + userId + "断开连接!当前在线人数为" + getOnlineCount());
	        }
	    }
	
	    /**
	     * 收到客户端消息后调用的方法
	     *
	     * @param message 客户端发送过来的消息
	     */
	    @OnMessage
	    public void onMessage(String message, Session session) {
	        log.info("收到来自窗口" + userId + "的信息:" + message);
	        if (StringUtils.isNotBlank(message)) {
	
	            MessageFormat format = JSONObject.parseObject(message, MessageFormat.class);
	            String text = format.getContentText();
	            try {
	                String toUserId = format.getToUserId();
	                String contentText = format.getContentText();
	                //传送给对应用户的websocket
	                if (StringUtils.isNotBlank(toUserId) && StringUtils.isNotBlank(contentText)) {
	                    WebSocketServer socketx = websocketList.get(toUserId);
	                    //需要进行转换,userId
	                    MessageFormat messageFormat = new MessageFormat();
	                    if (socketx != null) {
	                        messageFormat.setFromUserId(toUserId);
	                        messageFormat.setReceiveMessage(text);
	                        socketx.sendMessage(JSON.toJSONString(messageFormat));
	                    }else {
	                        messageFormat.setFromUserId("系统消息");
	                        messageFormat.setReceiveMessage("用户-" + userId + "-不在线");
	                        messageFormat.setMessageType("1");
	                        socketx.sendMessage(JSON.toJSONString(messageFormat));
	                    }
	                }
	            } catch (Exception e) {
	                e.printStackTrace();
	            }
	        }
	    }
	
	    /**
	     * 连接错误调用的方法
	     * @param session
	     * @param error
	     */
	    @OnError
	    public void onError(Session session, Throwable error) {
	        log.error("发生错误");
	        error.printStackTrace();
	    }
	
	    /**
	     * 实现服务器主动推送
	     */
	    public void sendMessage(String message) throws IOException {
	        this.session.getBasicRemote().sendText(message);
	    }
	
	
	    /**
	     * 群发自定义消息
	     */
	    public static void sendInfo(String id, String message) throws IOException {
	        log.info("推送消息到窗口:" + id + ",推送内容:" + message);
	
	        for (String str : websocketList.keySet()) {
	            try {
	                MessageFormat messageFormat = new MessageFormat();
	
	                messageFormat.setReceiveMessage(message);
	                messageFormat.setMessageType("2");
	                if (id == null) {
	                    messageFormat.setFromUserId("推送消息");
	                    websocketList.get(str).sendMessage(JSON.toJSONString(messageFormat));
	                } else if (str.equals(id)) {
	                    messageFormat.setFromUserId("用户-" + id + "-推送消息" );
	                    websocketList.get(str).sendMessage(JSON.toJSONString(messageFormat));
	                }
	            } catch (IOException e) {
	                continue;
	            }
	        }
	    }
	
	    public static synchronized int getOnlineCount() {
	        return onlineCount;
	    }
	
	    public static synchronized void addOnlineCount() {
	        WebSocketServer.onlineCount++;
	    }
	
	    public static synchronized void subOnlineCount() {
	        WebSocketServer.onlineCount--;
	    }
	}

前端部分也要实现websocket服务:
	<!DOCTYPE html>
	<html xmlns:th="http://www.thymeleaf.org">
	<head>
	    <meta charset="UTF-8">
	    <title>Title</title>
	    <script th:src="@{/js/jquery-2.1.1.min.js}"></script>
	</head>
	<p>
	    <script>
	        var socket;
	        var start = false;
	
	        function openSocket() {
	            if (typeof (WebSocket) == "undefined") {
	                console.log("您的浏览器不支持WebSocket");
	            } else {
	                console.log("您的浏览器支持WebSocket");
	                //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
	                //等同于socket = new WebSocket("ws://服务器IP/项目名/项目路径/注册ID");
	                if (start == true) {
	                    alert("您已连接成功,请不要重复连接!")
	                } else {
	                    socket = new WebSocket("ws://服务器IP/项目名/项目路径/" + $("#userId").val());
	                    //打开事件
	                    socket.onopen = function () {
	                        console.log("Socket 已打开");
	                        start = true;
	                        //socket.send("这是来自客户端的消息" + location.href + new Date());
	                    };
	                    //获得消息事件
	                    socket.onmessage = function (msg) {
	                        var message = JSON.parse(msg.data);
	                        console.log(msg);
	                        //发现消息进入    开始处理前端触发逻辑
	                        if(message.messageType == 0){
	                            $("#test").append(message.fromUserId + " ---> " +message.receiveMessage + "<br>") ;
	                        }else {
	                            alert(message.fromUserId + ":" + message.receiveMessage);
	                        }
	                    };
	                    //关闭事件
	                    socket.onclose = function () {
	                        console.log("Socket已关闭");
	                    };
	                    //发生了错误事件
	                    socket.onerror = function () {
	                        alert("Socket发生了错误");
	                        //此时可以尝试刷新页面
	                    }
	                }
	            }
	        }
	
	        function sendMessage() {
	            if (typeof (WebSocket) == "undefined") {
	                console.log("您的浏览器不支持WebSocket");
	            } else {
	                console.log("您的浏览器支持WebSocket");
	                console.log('{"toUserId":\"' + $("#toUserId").val() + '\","contentText":\"' + $("#contentText").val() + '\"}');
	                socket.send('{"toUserId":\"' + $("#toUserId").val() + '\","contentText":\"' + $("#contentText").val() + '\"}');
	                $("#test").append($("#contentText").val() + " <--- " + $("#userId").val() + "<br>");
	            }
	        }
	        function pushMessage() {
	            if (typeof (WebSocket) == "undefined") {
	                console.log("您的浏览器不支持WebSocket");
	            } else {
	                console.log("您的浏览器支持WebSocket");
	                $.post("http://服务器IP/推送服务路径",
	                    {'id':'','message':$("#contentText").val()},function (result) {
	                        if(result){
	                            alert("推送成功");
	                        }else{
	                            alert("推送失败")
	                        }
	                    })
	            }
	        }
	
	        function test() {
	            if (typeof (WebSocket) == "undefined") {
	                console.log("您的浏览器不支持WebSocket");
	            } else {
	                console.log("您的浏览器支持WebSocket");
	                socket.send('{"toUserId":\"' + $("#toUserId").val() + '\","contentText":"用户下线"}');
	                socket.close();
	                start = false;
	            }
	        }
	
	    </script>
	
	
	    【当前用户ID】:<input id="userId" name="userId" type="text"><br>
	    【接收用户ID】:<input id="toUserId" name="toUserId" type="text"><br>
	
	    【操作】:<input type="button" onclick="openSocket()" value="加入连接">
	    <input id="bt" type="button" onclick="test()" value="断开连接"><br>
	    【接收消息内容】:
	<div id="test"></div>
	
	<br>
	<input id="contentText" name="contentText" type="text">
	<input type="button" onclick="sendMessage()" value="发送消息">
	<input type="button" onclick="pushMessage()" value="推送消息">
	
	</body>
	</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值