精华内容
下载资源
问答
  • WebSocket双向通信原理初探

    千次阅读 2018-03-20 19:23:05
     双向通信如何实现先介绍下,TCP/IP参考模型 TCP是一个,相对可靠确保信息送达、按照顺序送达的中层信息传输协议,但性能比UDP较差。它负责两端主机建立会话。我们的websocket处于应用层,也是基于TCP实现的。TCP...

    前言

    WebSocket被广泛用于web的实时消息通信系统中。
     
    它实现了浏览器与服务器全双工通信,将会替代基于http的ajax长轮询的拉取消息模式。
     

    双向通信如何实现

    先介绍下,TCP/IP参考模型
     
    TCP是一个,相对可靠确保信息送达、按照顺序送达的中层信息传输协议,但性能比UDP较差。它负责两端主机建立会话。
    我们的websocket处于应用层,也是基于TCP实现的。

    TCP连接

    采用TCP协议,在真正的读写操作之前,server与client之间必须建立一个连接。 读写完成后,双方不再需要这个连接时,就可以释放这个连接。

    TCP短连接

    流程:client向server发起TCP连接请求,client向server发送消息,server回应client,然后一次读写就完成了。这时候client会发起close操作,连接关闭。
    短连接的优点是:
    • 管理起来比较简单,连接都是有用的连接,不需要额外的控制手段
    • HTTP1.0就是基于此实现的,他是是无状态的。浏览器和服务器每一次HTTP操作,就建立一次连接,但任务结束就中断连接。

    TCP长连接:

    流程:client向server发起连接,server接受client连接。双方建立连接,Client与server完成一次读写之后,它们之间的连接并不会主动关闭,用心跳保活。后续的读写操作会继续使用这个连接。
     
    WebSocket就是基于TCP连接建立通讯的。
    一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。在客户端或Server端中断连接前,不需要客户端和服务端重新发起连接请求。

    WebSocket建立连接

    websocket与http同样建立于tcp传输协议之上,通过tcp传输层进行数据传输。
    我们要使用websocket协议进行通信则首先要建立起websocket连接,这个连接的建立依赖于http。
     
    发起握手:
    每个WebSocket连接都始于一个HTTP请求。
    具体来说,WebSocket协议在第一次握手连接时,通过HTTP协议在传送数据,但是比普通HTTP请求相比多了一些字段。
            GET /chat HTTP/1.1
            Host: server.example.com
            Upgrade: websocket
            Connection: Upgrade
            Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
            Origin: http://example.com
            Sec-WebSocket-Protocol: chat, superchat
            Sec-WebSocket-Version: 13
     
    服务器响应:
    根据特殊的请求头进行了特殊响应,首先101返回码表明本次连接的通信协议经过了转换并成功握手成功建立起了通信。
    connection字段和upgrade字段则表明本次通信协议进行了升级转换,转换的是websocket协议。
    服务器响应头如下
            HTTP/1.1 101 Switching Protocols
            Upgrade: websocket
            Connection: Upgrade
            Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
            Sec-WebSocket-Protocol: chat
     
    建立了websocket连接后,只要客户端和服务器端任意一端不主动断开连接前,通信行为都是在一个持久连接上发起,后续数据与请求都通过帧序列的形式进行传输。

    总结

    HTTP 1.1也支持了长连接(PersistentConnection),在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,但仍然还是无状态的。
    WebSocket设计上天生为HTTP增强通信,将会在即时IM通讯,游戏等领域会得到更广泛的利用
     
    参考:
     
     
     
    展开全文
  • 会展示一个,个人理解的WebSocket最简单的用法。 重点 1、JAVA内的WebSocket是在Java jar7.0之后才能使用的。 2、需要在JavaServer项目lib目录下引入 javaee-api-7.0 包 |前端代码...

    本文的提出是自己学习上领会的东西,不代表全部(日后改善)。会展示一个,个人理解的WebSocket最简单的用法。

    重点
    1、JAVA内的WebSocket是在Java jar7.0之后才能使用的。
    2、需要在JavaServer项目lib目录下引入 javaee-api-7.0

    |前端代码

     <script>
            function creatRoom() {
    
                var webSocket; //声明一个变量等下来用来 之后放WebSocket实例化对象房间
                var roomName = "MainBlock" /* 为例比较简单的 说明 这里写死一个房间号,根据自己项目需要进行动态更改 */
                if ("WebSocket" in window) { //查看window对象是否有websocket
                    if (webSocket == null) {
                        console.log("房间号:" + roomName)
                        var url = "ws://172.16.245.232:8080/WebsocketPro/webSocket/mainBlock/" + roomName; //向后台请求的连接 创建该房间
                        console.log("房间地址:" + url)
                        // 打开一个 web socket 
                        webSocket = new WebSocket(url); //放WebSocket实例化对象房间
                        console.log("成功创建WebSocket对象:")
                    } else {
                        console.log("已经创建了WebSocket的实例化对象...")
                    }
    
                    webSocket.onopen = function () {
                        console.log("和服务器的握手连接建立...(握手)")
                    };
                    
                    webSocket.send(msg); //这个msg是自己定义的形参 向后台发送消息
    
                    webSocket.onmessage = function (evt) {
                        var Data = JSON.parse(evt.data)
    
                    };
    
                    webSocket.onclose = function () {
                        console.log("和服务器的握手连接已关闭...(分手)")
                        webSocket = null;
                    };
                } else {
                    // 浏览器不支持 WebSocket
                    console.log("浏览器不支持 WebSocket!");
                }
    
                // 看看有没有穿件房间对象
                if (webSocket != null) {
                    console.log("已经创建房间");
                } else {
                    console.log("没有创建房间");
                }
            }
        </script>
    

    |后端JAVA代码

    package websocketPro;
    
    import javax.websocket.OnClose;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    /**
     * writer: holien Time: 2017-08-01 13:00 Intent: webSocket服务器
     */
    @ServerEndpoint("/webSocket/ChatRoom/{roomName}")//这里是你请求的生成的房间服务器路径 roomName是前端发来的 roomName名字
    public class ChatRoom {
    	private static final Map<String, Set<Session>> rooms = new ConcurrentHashMap();
    	
    	@OnOpen //session 是可选的参数,session为与某个客户端的连接会话,需要通过它来给客户端发送数据 
    	public void connect(@PathParam("roomName") String roomName, Session session) throws Exception {
    		if (!rooms.containsKey(roomName)) {
    			Set<Session> room = new HashSet<>();
    			room.add(session);
    			rooms.put(roomName, room);
    		} else {
    			rooms.get(roomName).add(session);
    		}
    		System.out.println("建立连接 !");
    	}
    
    	@OnClose
    	public void disConnect(@PathParam("roomName") String roomName, Session session) {
    		rooms.get(roomName).remove(session);
    		System.out.println("断开连接!");
    		
    	}
    
    	@OnMessage
    	public void receiveMsg(@PathParam("roomName") String roomName, String msg, Session session) throws Exception {
    		System.out.println(msg);
    		broadcast(roomName, msg);
    	}
    
    	// 按照房间名进行广播
    	public static void broadcast(String roomName, String msg) throws Exception {
    		for (Session session : rooms.get(roomName)) {
    			session.getAsyncRemote().sendText(msg);
    		}
    	}
    
    }
    

    |JAVA服务器目录结构
    在这里插入图片描述

    Tip:下面简单的解释一下每个函数及其方法的含义和关联。

    所有WebSocket项目双向通信的建立,都需要前端先发送给后端一个请求,并且握手成功之后才能进行通信 就和打电话一个道理。

    webSocket = new WebSocket(url);
    

    上面这个就是前端JS创建一个WebSocket对象 并且发送给后端(URL地址的服务器)一个通信请求 你正在向一个人打电话,并且等他接电话

    @ServerEndpoint("/webSocket/ChatRoom/{roomName}")
    
    var url = "ws://172.16.245.232:8080/WebsocketPro/webSocket/mainBlock/" + roomName;
    

    后端java这个注释标记了当前这个Java文件的服务器访问地址和前端的请求的地址是一致的。
    前端定义 roomName 的名称会被后端的 {roomName} 读取到
    之前 前端的roomName定义为 MainBlock
    所以在请求之后后端注释内的访问路径就变成了
    /webSocket/ChatRoom/MainBlock

    private static final Map<String, Set<Session>> rooms = new ConcurrentHashMap();
    @OnOpen
    public void connect(@PathParam("roomName") String roomName, Session session) throws Exception {
    		if (!rooms.containsKey(roomName)) {
    			Set<Session> room = new HashSet<>(); //创建Hash数组 的数理化对象
    			room.add(session); //把session对象 即用户对象 添加到Hash对象里
    			rooms.put(roomName, room); //并且根据房间名添加用户组成的 Hash对象  这里就获取了一个用户集
    		} else {
    			rooms.get(roomName).add(session); //获取对应房间的 用户集
    		}
    		System.out.println("建立连接 !");
    	}
    

    关于 ConcurrentHashMap 参考ConcurrentHashMap
    此时在申请之后会被后端的 @OnOpen注释下的方法接收到
    这个时候底层WebSocket会进行和前端的握手协议 我们是看不到的
    诺访问正常并且握手成功,则服务器会自动调用这个连接方法
    此时底层会向服务器发送成功握手的协议
    并且前端也会调用下面这个定义的的方法

    webSocket.onopen = function () {
    	console.log("和服务器的握手连接建立...(握手)")
    };
    

    到这里为止 前端和后端建立了一个理论上永久的通信协议,只要其中一方不要断开连接。 你成功和他打通了电话

    |开始交流

    webSocket.send(msg);
    

    我们前端调用了WebSocket对象的 send() 方法 传入要发送给后台的消息

    封装要发送到服务端的数据一般来说JSON比较好
    
    @OnMessage
    	public void receiveMsg(@PathParam("roomName") String roomName, String msg, Session session) throws Exception {
    		System.out.println(msg);
    		broadcast(roomName, msg);
    	}
    

    传入的消息就会被后台的 @OnMessage 标记的方法接收

    @PathParam(“roomName”) String roomName

    这句话的意思就是之前标记的 /webSocket/ChatRoom/{roomName} 里的 roomName 的值

    String msg

    这个参数就是前台发送的消息。 Session session 这个参数在代码中说明了就不重复解释
    在接收到消息之后就调用这个这个函数内的一个完美自定义的方法 broadcast()

    public static void broadcast(String roomName, String msg) throws Exception {
    	for (Session session : rooms.get(roomName)) {
    		session.getAsyncRemote().sendText(msg);
    	}
    }
    

    这个方法调用了 session 对象里的 getAsyncRemote() 方法的 sendText(msg)
    用来向之前封装的所有对象( rooms )广播消息

    webSocket.onmessage = function (evt) {
    	var Data = JSON.parse(evt.data)
    };
    

    接收到广播消息之后,前端调用了 WebSocket对象的 onmessage 方法
    这里的 evt 参数就是广播的消息 但是后端传过来的是一个对象
    我们要调用这个对象的 evt.data 方法 如果是Json数据 就把它转换成我们js能识别的对象

    到这里为止来就完成了消息的交流。

    要断开请求的话,就调用 webSocket.close() 方法 ,这个时候
    后台就会自动调用 下面这个方法

    @OnClose
    	public void disConnect(@PathParam("roomName") String roomName, Session session) {
    		 
    		System.out.println("断开连接!");
    		
    	}
    

    同时响应到前端 ,调用下面这个方法

    webSocket.onclose = function () {
            console.log("和服务器的握手连接已关闭...(分手)")
            webSocket = null;
    };
    

    到这里来位置完成了WebSocket 完整的双向通信。

    展开全文
  • 服务端使用tomcat7.0 + jsp 客户端使用html5 使用websocket,实现客户端和服务端的双向通信
  • 事件 WebSocket.onclose:链接关闭后触发 WebSocket.onerror:链接失败时触发 WebSocket.onmessage:从服务器接受到消息时触发 WebSocket.onopen:链接成功时触发 // 使用官方提供的已配置好的可用于测试的websocket...

    1.背景

    之前我们说HTTP协议的时候提到,服务器默认是不能主动给客户端发送请求的,但有些应用场景,需要服务器具备这样的功能

    如果要做一个简易的聊天小功能,双方聊天都会将消息先推到服务器,再发送给对方,而对方不一定是秒回的,可能过很久才回复;
    由于服务器是不能向客户端发送请求的,只有客户端才能发起请求,每次我发一条消息到服务器,对方没回,客户端和服务器就断开联系,就需要后续客户端不断地询问,对方是否有消息了,这样很浪费性能,也容易造成网络拥塞;
    即使说,设置一个等待时间,等一等看看对方有没有消息回来,如果有,就返回,链接断开;如果没有,到了规定时间也返回,链接断开,这样对服务器来说,负担也是很大的

    所以,后来引入了websocker的概念,能够支持服务器向客户端发送请求,广泛应用于实时传输消息,是基于HTTP1.1的协议

    2.websocket连接过程

    2.1客户端先和服务器建立TCP链接
    2.2客户端通过http协议和服务器确认协议版本号,数据格式,本地地址等等信息,客户端先发送请求,服务器确认后接受连接请求,发回消息告诉客户端,客户端确认后本次握手连接成功(类似又一次三次握手)
    2.3连接成功则触发onopen事件,可以正常传输数据,除非一方断开连接,否则连接一直保持;
    2.4连接失败触发onerror事件,返回失败原因

    3.基本使用

    1.创建websocket
    var socket = new WebSocket(“ws://echo.websocket.org”);
    2.websocket方法
    socket.send() 传输数据
    socket.close() 终止任何现有连接
    3.事件
    WebSocket.onclose:链接关闭后触发
    WebSocket.onerror:链接失败时触发
    WebSocket.onmessage:从服务器接受到消息时触发
    WebSocket.onopen:链接成功时触发

    // 使用官方提供的已配置好的可用于测试的websocket服务器,功能是将客户端接收的消息返回给客户端
    var socket = new WebSocket("ws://echo.websocket.org");
    // 当链接建立成功时,触发onopen事件
    socket.onopen = function(){
        console.log(88);
        socket.send( "test" ); //发送数据
        // socket.close(); //关闭websocket连接
    }
    // 接收到消息时触发
    socket.onmessage = function(messageEvent){
        console.log(messageEvent);
    }
    // 断开连接时触发
    socket.onclose = function(){
        console.log('连接已断开')
    }
    

    4.优点

    1.客户端与服务器都可以主动传送数据给对方
    2.不用频繁创建TCP请求和销毁请求,减少网络带宽资源的占用,同时也节省服务器资源

    好文链接
    好文链接
    好文链接

    展开全文
  • springboot+websocket双向通信实现消息推送功能 项目中如果需要假如消息推送功能,有时会用到websocket,这是一种长连接方式与服务器进行连接,优点:实时性较高,如果无数据更新时,并不会频繁进行请求,而只要...

    springboot+websocket双向通信实现消息推送功能

    项目中如果需要假如消息推送功能,有时会用到websocket,这是一种长连接方式与服务器进行连接,优点:实时性较高,如果无数据更新时,并不会频繁进行请求,而只要数据进行更新,那么服务器就会想客户端发送请求,而这样的方式是以服务器资源作为代价来保证实时性。
    前端代码

    //webSocket对象
    var websocket = null;
    var userId;
    //避免重复连接
    var lockReconnect = false, tt;
    
    createWebSocket();
    /**
     * webSocket重连
     */
    function reconnect() {
        if (lockReconnect) {
            return;
        }
        lockReconnect = true;
        tt && clearTimeout(tt);
        tt = setTimeout(function () {
            console.log('重连中...');
            lockReconnect = false;
            createWebSocket();
        }, 4000);
    }
    
    /**
     * websocket心跳检测
     */
    var heartCheck = {
        timeout: 3000,
        timeoutObj: null,
        serverTimeoutObj: null,
        reset: function () {
            clearTimeout(this.timeoutObj);
            clearTimeout(this.serverTimeoutObj);
            return this;
        },
        start: function () {
            var self = this;
            this.timeoutObj && clearTimeout(this.timeoutObj);
            this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
            this.timeoutObj = setTimeout(function () {
                //这里发送一个心跳,后端收到后,返回一个心跳消息,
                websocket.send("connect");
                self.serverTimeoutObj = setTimeout(function () {
                
                }, self.timeout)
            }, this.timeout)
        }
    };
    
    function createWebSocket() {
        //判断当前浏览器是否支持WebSocket
        if ('WebSocket' in window) {
    
            var pathName = window.document.location.pathname;
    
            var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1);
            
           //动态获取websocket服务地址
             var url = "ws://" + window.document.location.host + projectName + "/webSocket/" + userId;
             // 浏览器支持Websocket
             websocket = new WebSocket(url);
    
             //WebSocket连接发生错误的回调方法
             websocket.onerror = function () {
                 reconnect();
             };
    
             //WebSocket连接成功建立的回调方法
             websocket.onopen = function () {
                 //userId是用户的id也可以是token,按需设置,只要是唯一用户标识
                 websocket.send('{"toUserId":"' + userId + '"}')
                 //心跳检测重置
                 heartCheck.reset().start();
             };
    
             //接收到消息的回调方法
             websocket.onmessage = function (event) {
                 //这里的event则是服务器所发送过来的消息
                 console.log('消息内容:'+event);
                 heartCheck.reset().start();
             };
    
             //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
             window.onbeforeunload = function () {
                 closeWebSocket();
             };
    
             //关闭WebSocket连接
             function closeWebSocket() {
                 websocket.close();
             }
    
             //连接关闭的回调方法
             websocket.onclose = function () {
                 heartCheck.reset();//心跳检测
                 reconnect();
             };
        } else {
            console.log("您的浏览器暂不支持webSocket");
        }
    }
    

    后端代码

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import javax.websocket.*;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    @Component
    @ServerEndpoint(value = "/webSocket/{userId}", configurator = MySpringConfigurator.class)
    public class WebSocket {
        /**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
        private static ConcurrentHashMap<String,WebSocket> webSocketMap = new ConcurrentHashMap<>();
        /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
        private Session session;
        /**接收userId*/
        private String userId="";
    
        /**
         * 连接建立成功调用的方法*/
        @OnOpen
        public void onOpen(Session session,@PathParam("userId") String userId) {
            this.session = session;
            this.userId=userId;
            if(webSocketMap.containsKey(userId)){
                webSocketMap.remove(userId);
                webSocketMap.put(userId,this);
            }else{
                webSocketMap.put(userId,this);
            }
        }
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            if(webSocketMap.containsKey(userId)){
                webSocketMap.remove(userId);
            }
        }
        
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message 客户端发送过来的消息*/
        @OnMessage
        public void onMessage(String message, Session session) {
            //收到消息的内容可以自定义
        }
    
        /**
         * 连接失败调用的方法
         * @param session
         * @param error
         */
        @OnError
        public void onError(Session session, Throwable error) {
            error.printStackTrace();
        }
        /**
         * 服务器主动推送消息
         */
        public void sendMessage(String message) throws IOException {
            this.session.getBasicRemote().sendText(message);
        }
    
        /**
         * 向用户发送自定义消息
         * */
        public static void sendInfo(String message,@PathParam("userId") String userId) throws IOException {
            if(webSocketMap.containsKey(userId)){
                webSocketMap.get(userId).sendMessage(message);
            }else{
    
            }
        }
    }
    

    这里有个需要注意的地方,就是webSocket在连接一定时间后未收到服务器消息时会自动断开连接,那么则需要假如心跳检测机制。

    展开全文
  • 双向通信websocket

    2019-05-07 08:49:19
    简介 定义一个API,用以在网页浏览器和服务器之间建立socket连接,这个...支持双向通信,实时性强 更好的二进制支持 没有同源限制,客户端可以与任意服务器通信 较少的控制开销(创建后,ws客户端\服务端进行数据交换时,...
  • 一、websocket介绍 通常服务端想主动给前端推送信息的话,需要使用轮询技术,即在特定的时间间隔(比如每1秒),浏览器通过调用服务端的接口向服务端发送http请求,服务端将最新的数据发送给浏览器。这种方式在性能...
  • pom 文件 org.springframework.boot spring-boot-starter-web 2.2.5.RELEASE org.springframework.boot spring-boot-starter-thymeleaf 2.2.5.RELEASE org.springframework.boot spring-boot-starter-websocket ...
  • 主要介绍了Vue+Java 通过websocket实现服务器与客户端双向通信操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 下面记一下一个简单的基于GatewayWorker框架写的websocket的程序。 传统的websocket使用方式都是 1.客户端像服务器发送http请求,并发起websocket链接请求。 然后php框架调用GatewayWorker进行uid和websock...
  • websocket1. vue代码2. java代码 1. vue代码 methods: { //在方法里调用 this.websocketsend()发送数据给服务器 onConfirm () { //需要传输的数据 let data = { code: 1, item: ‘传输的数据’ }...
  • 主要给大家爱介绍了关于WebSocket通信过程与实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧
  • 最近工作中需要和服务器交互、传输数据,服务器端用的就是WebSocket,在此之前也是在网上翻翻找找,还是BestHttp这个插件好用、在此记录下,当做记笔记了、代码内容也是参考其他文章,也算是前人栽树后人乘凉了。...
  • 有一个需求 服务端是用java写的 用的WebSocket协议通信 服务商只提供了数据包的数据结构 我想知道websocket用c++怎么建立连接 会的最好提供下实例代码 拜托了
  • WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是基于 TCP 的一种独立实现。 以前客户端想知道服务端的处理进度,要不停地使用 Ajax 进行轮询,让浏览器隔个几...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,659
精华内容 4,663
关键字:

websocket双向通信