一概述

## 在项目中有这样一个需要,app为智能心电跑步类app,在跑步时需要在网页端显示用户的跑步信息,包括跑步时长、心率、心电图等,其中心电图是需要实时更新的。当用户开始跑步时在网页上显示这些信息,跑步结束时结束实时数据显示,将最终跑步结果显示在网页时,效果图如下:



在网页上的显示效果如下:


先做个大致的分析,数据要做到实时显示,需要将app产生的数据通过网络发送到服务器,然后网页从服务器获取数据,在网页上显示,如下图所示:

整个流程比较简单,但是实现起来却比较难。这里选择实现技术很重要,先按照一般思路,app像服务器发送数据,通过http协议不断的向服务器发送数据,网页端不断的通过http向服务器请求数据,这时候你会发现app要实时发送数据通过http很耗资源甚至是几乎做不到,app每个几秒要向服务器发送运动数据,还有心电图实时显示通过http是做不到的,网页也是一样,这时遇到瓶颈,这种需要应该很常用,向app上很常用的消息推送、在线下线通知,他们的实现方式也肯定不是发生http请求,这时这个全新的问题摆在自己面前,该如何解决?查资料,然后会发现一个专门针对这种场景的技术,叫websocket。下面我们先来了解websocket,然后讨论通过websocket来实现需求的大致思路,然后在app端、网页端、服务器分析实现具体的技术细节。

二、websocket原理

WebSocket用于在Web浏览器和服务器之间进行任意的双向数据传输的一种技术。WebSocket协议基于TCP协议实现,包含初始的握手过程,以及后续的多次数据帧双向传输过程。其目的是在WebSocket应用和WebSocket服务器进行频繁双向通信时,可以使服务器避免打开多个HTTP连接进行工作来节约资源,提高了工作效率和资源利用率。

注:下面这部分websocket结束有参考与博客https://segmentfault.com/a/1190000007614996

1. 什么是keep-alive

keep-alive只是客户端的一种建议

我们打开百度首页,进一步查看header。

如图,我们看到请求header中有一行:

Connection:keep-alive

keep-alive是通知服务器,在这个HTTP Request/Responset结束后,不要立即断开TCP连接(注意是TCP连接,和HTTP没有关系),后面的HTTP Request仍然可以通过这个TCP连接继续传送。

但是!这只是个建议,服务器可能不支持,也可能忽略掉这个建议。也可能因为时间太久而直接断开TCP连接

通俗点解释就是:keep-alive只是通知服务器,您先别挂,一会儿可能还有活儿,至于它挂不挂还是看它心情。
所以,keep-alive只是客户端建议的一种复用TCP连接的方式,至于服务器支持不支持,就由不得客户端了。

keep-alive只是http协议中的一部分
keep-alive是http协议中的一部分,也即客户端可以主动的发起request到服务器,服务器只能被动的response给客户端。
我要想实现服务器主动的push消息给客户端,keep-alive是无能无力的。

2. 服务器的消息如何发给客户端

long long ago~ 服务器端要想主动的push消息给客户端(比如网页聊天室消息的即时收发),这是不可能滴。
但是,我可以使用ajax轮询、long poll 技术造一个服务端给客户端主动push消息的假象。

①:什么是ajax轮询?

ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。

场景再现:

客户端:啦啦啦,有没有新信息(Request) 服务端:没有(Response) 客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response) 客户端:啦啦啦,有没有新信息(Request) 服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request) 服务端:好啦好啦,有啦给你。(Response)
客户端:啦啦啦,有没有新消息(Request) 服务端:。。。。。没。。。。没。。。没有(Response) —- loop

但是这样,有没有发现,大大增加了服务端的负载,并且速度还慢。

②:什么是long poll?

long poll和ajax差不多,原理都是采用轮询的方式。只不过long poll是采取的阻塞的方式去轮询。
也即客户端发起一个请求连接,这个连接会阻塞住,直到服务端有了消息,才会response给客户端。

场景再现:

客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) 服务端:额。。 等待到有消息的时候。。来
给你(Response) 客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop

long pull 虽然降低了服务器的负载,但是需要服务器有很高的并发能力才可以。
而目前处理高并发的模型基本都是异步非阻塞的模型(比如nginx)。

③:总结
ajax轮询、long poll技术虽然都能实现服务端消息的实时通知,但是各有缺点,都不是根本的解决办法。
计算机界急需一种新的技术去处理这些需求~

3. websocket来啦

既然ajax轮询、long poll都不怎么样。我们发明一种新的协议吧!

Websocket协议解决了服务器与客户端全双工通信的问题。

注:什么是单工、半双工、全工通信? 信息只能单向传送为单工; 信息能双向传送但不能同时双向传送称为半双工;
信息能够同时双向传送则称为全双工。

websocket协议解析
wensocket协议包含两部分:一部分是“握手”,一部分是“数据传输”。
为了便于演示,我们采用swoole建立一个websocket服务器来演示。

第一步、握手

①客户端向服务端发起连接请求

如图,我们在请求服务器的时候,发送了这样的request header。

下面我们就一些比较重要的字段信息进行说明:

Connection:Upgrade #通知服务器协议升级 Upgrade:websocket #协议升级为websocket协议
Host:0.0.0.0:9501 #升级协议的服务主机:端口地址
Sec-WebSocket-Key:K8o1cNIxO2pR6inTIDBSgg== #传输给服务器的key
Sec-WebSocket-Version:13 #websocket协议版本13

Sec-WebSocket-Key有什么用呢?
客户端将这个key发送给服务器,服务器将这个key进行处理,将处理后的key返回给客户端,客户端根据这个key是否正确来判断是否建立连接。

②:服务端返回握手应答

如图,我们看到websocket协议状态码是101.

101表示协议切换成功。

我们查看websocket的response header。如图:

下面解释下reponse header字段的含义

Connection:Upgrade #协议升级成功
Sec-WebSocket-Accept:GnoYH/ip/ZMh+a5rX5P/YR6e68g= #服务端处理之后的key
Sec-WebSocket-Version:13#websocket 协议版本号
Upgrade:websocket#协议升级为websocket

至此,websocket握手成功!下面就尽情的传输数据吧!

第二步、数据传输

连接建立成功之后,就可以进行数据传输了,不同的客户端有不同的实现方案,在Android、iOS、网页(通过js)都可以向服务器发送数据,当然服务器可以是java、PHP等,客户端和服务器端都有相应的解决方案。

三、实现方案概述

通过上面对websocket的了解,websocket是一种类似http新的协议,代替以前网络轮训或长连接代码的资源消耗,专门用来做一些实时性很强的数据传输,可以建立webSocket连接,然后数据通过建立的webSocket通道进行传输。大致流程如下:

如上图,app和服务器建立websocket连接后,会得到websocket对象,然后app端通过websocket对象向服务器发送数据。网页端建立websocket的过程和app端一致,得到websocket对象后,这个websocket通过会处于监听状态,一致监听服务器是否有数据到来。当图中app发送数据到服务器(标号为3,绿色线),服务器会通过和网页端建立的websocket对象向网页发送一条数据(标号为6,绿色线),然后网页端会收到服务器发送的数据。

注:上图通信过程中总共建立2个websocket连接,一个是app端和服务器端建立的连接,建立后app端会有一个websocket对象,服务器端有一个websocket对象。一个是网页端和服务器端建立的连接,建立后网页会有一个websocket对象,服务器端有一个websocket对象。所以,服务器端此时有2个websocket对象,达到app端的数据向网页端转发的功能。

三、实现细节(app、网页、服务器)

1. app

app端采用Android技术实现,Android端提供websocket通信相关的库,为JavaWebSocket_fat.jar,可以导入进来直接使用。

public class WebSocketUtil {private static final String TAG = "WebSocketUtil";public  WebSocketClient mWebSocketClient;public boolean isStartDataTransfer;public String mCurBrowserClientID;public String mCurAppClientID;//建立websocket连接public void connectWebSocket(String address) {try {initSocketClient(address);} catch (URISyntaxException e) {e.printStackTrace();}new Thread(){@Overridepublic void run() {mWebSocketClient.connect();}}.start();}public void initSocketClient(String address) throws URISyntaxException {if(mWebSocketClient == null) {//建立成功会得到一个websocket的客户端对象,以后发消息都是通过这个对象来发送mWebSocketClient = new WebSocketClient(new URI(address)) {@Overridepublic void onOpen(ServerHandshake serverHandshake) {//连接成功Log.i(TAG,"opened connection");}@Overridepublic void onMessage(String s) {//收到服务端消息Log.i(TAG,"received:" + s);}@Overridepublic void onClose(int i, String s, boolean remote) {//连接断开,remote判定是客户端断开还是服务端断开Log.i(TAG,"Connection closed by " + ( remote ? "remote peer" : "us" ) + ", info=" + s);}@Overridepublic void onError(Exception e) {Log.e(TAG,"error:" + e);}};}}//给服务器发送消息public void sendSocketMsg(String msg) {Log.i(TAG,"sendSocketMsg");if (mWebSocketClient!=null && mWebSocketClient.isOpen()){mWebSocketClient.send(msg); //通过mWebSocketClient发送数据Log.i(TAG,"msg:"+msg);}}//断开连接public void closeConnectWebSocket() {try {if(mWebSocketClient!=null){mWebSocketClient.close();mWebSocketClient = null;}} catch(Exception e) {e.printStackTrace();} finally {mWebSocketClient = null;isStartDataTransfer = false;}}

2. 网页

网页端端HTML5提供了websocket的实现方案,直接调用js的函数即可建立连接、发送数据等,会发现网页端和app的websocket建立连接过程极为相似。

<%@ page language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head><title>Java后端WebSocket的Tomcat实现</title>
</head>
<body>Welcome<br/><input id="text" type="text"/><button onclick="send()">发送消息</button><hr/><button onclick="closeWebSocket()">关闭WebSocket连接</button><hr/><div id="message"></div>
</body><script type="text/javascript">var websocket = null;//判断当前浏览器是否支持WebSocketif ('WebSocket' in window) {websocket = new WebSocket("ws://localhost:8080/websocket");}else {alert('当前浏览器 Not support websocket')}//连接发生错误的回调方法websocket.onerror = function () {setMessageInnerHTML("WebSocket连接发生错误");};//连接成功建立的回调方法websocket.onopen = function () {setMessageInnerHTML("WebSocket连接成功");}//接收到消息的回调方法websocket.onmessage = function (event) {setMessageInnerHTML(event.data);}//连接关闭的回调方法websocket.onclose = function () {setMessageInnerHTML("WebSocket连接关闭");}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function () {closeWebSocket();}//将消息显示在网页上function setMessageInnerHTML(innerHTML) {document.getElementById('message').innerHTML += innerHTML + '<br/>';}//关闭WebSocket连接function closeWebSocket() {websocket.close();}//发送消息function send() {var message = document.getElementById('text').value;websocket.send(message);}
</script>
</html>

3. 服务器

服务器端使用Java来写后台,用tomcat做服务器,tomcat7.0以上都提供了websocket实现库,这个库是tomcat自带的,直接使用即可,无需导入,如图:

所以这个功能比较常用,tomcat都列为自带库名单了。

package me.gacl.websocket;import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;/*** @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端*/
@ServerEndpoint("/websocket")
public class WebSocketTest {//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。private static int onlineCount = 0;//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();//与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;/*** 连接建立成功调用的方法* @param session  可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据*/@OnOpenpublic void onOpen(Session session){this.session = session;webSocketSet.add(this);     //加入set中addOnlineCount();           //在线数加1System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());}/*** 连接关闭调用的方法*/@OnClosepublic void onClose(){webSocketSet.remove(this);  //从set中删除subOnlineCount();           //在线数减1System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());}/*** 收到客户端消息后调用的方法* @param message 客户端发送过来的消息* @param session 可选的参数*/@OnMessagepublic void onMessage(String message, Session session) {System.out.println("来自客户端的消息:" + message);//群发消息for(WebSocketTest item: webSocketSet){try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();continue;}}}/*** 发生错误时调用* @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error){System.out.println("发生错误");error.printStackTrace();}/*** 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。* @param message* @throws IOException*/public void sendMessage(String message) throws IOException{this.session.getBasicRemote().sendText(message);//this.session.getAsyncRemote().sendText(message);}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocketTest.onlineCount++;}public static synchronized void subOnlineCount() {WebSocketTest.onlineCount--;}
}

看完三个代码,会发现app、网页、服务器的websocket代码极为相似,知道原理后代码写起来很简单。

到此所以的都介绍完了,上面app端、网页端、服务器端项目都是公司实际项目,无法上传分享,只做了简单的demo代码,有问题的私信或者发邮箱1812507678@qq.com。

通过webSocket实现app运动数据在网页实时显示相关推荐

  1. 通过webSocket实现app产生的数据在网页实时显示

    一概述 ## 在项目中有这样一个需要,app为智能心电跑步类app,在跑步时需要在网页端显示用户的跑步信息,包括跑步时长.心率.心电图等,其中心电图是需要实时更新的.当用户开始跑步时在网页上显示这些信 ...

  2. Java读取mapinfo格式_求助:如何把mapinfo格式的地图数据放到网页中显示

    声明,我可不是做广告哦 mapXtreme Java,是目前用于Internet或企业Intranet唯一的100%纯Java地图服务器.mapInfo mapXtreme Java与J2EE兼容,为 ...

  3. Arduio ESP8266将DHT11温湿度传感器数据在网页上显示

    Arduio ESP8266将DHT11温湿度传感器数据在网页上显示

  4. python web 服务器实时监控 websocket_python websocket网页实时显示远程服务器日志信息...

    功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息 一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看.你还在用 ...

  5. PHP Web网页实时显示海康摄像头监控画面

    首先下载VLC插件(无论电脑32位还是64位,安装32位即可) 链接:https://pan.baidu.com/s/1JHncnGxgeMqmMMCsvMzuWA 提取码:nk66 HTML部分 摄 ...

  6. 用VC++6.0的MFC框架编写的采集NI数采卡的数据程序曲线实时显示

    利用MFC编写 的采集程序,读取NI卡中的数据,其中的两个通道分别连接了1.5V的小电池作为电压输入信号,其他的通道悬空.将采集的数据实时显示在对话框中,如下图.需要源程序加我QQ 355866246 ...

  7. 单片机数据在网页上显示_不务正业的Excel也玩地图,在地图上显示数据,好玩又简单...

    Excel的尴尬 Excel越来越不象Excel,我们对Excel的第一映象就是制表.数据分析.常规操作自不必说,高级一点的无非就是透视图表.VBA等.不管再怎么高级,Excel似乎都是围绕数字在操作 ...

  8. 传感器采集保存数据与前端实时显示动态曲线图实现想法

    传感器1秒钟采集一次并且前端实时动态显示曲线图实现想法 首先,前端采用1.websocket来实现实时同步实现动态曲线变化.                     使用socket比http连接的次 ...

  9. MATLAB利用串口接收数据,并实时显示图形

    matlab读取串口资源总结: 一.matlab读取串口数据,并显示实时图形:点击这里打开 二.使用matlab进行串口调试的2中方法:点这里打开 三.matlab读取单片机串口数据并显示出来(m语言 ...

最新文章

  1. 20145328 《网络对抗技术》恶意代码分析
  2. 在循环中删除list中的元素
  3. java: http请求和响应
  4. ipython和anaconda区别_Anaconda和ipython环境适配的实现
  5. 一个老外如何丑化中国程序员
  6. QT的QGraphicsItemAnimation类的使用
  7. oauth2.0里回调地址返回code中如何让code不显示在URL里?
  8. jquery 页面滚动条回到顶部_jquery懒加载、回到顶部
  9. 沈南鹏:移动互联网会带来9大创新性的领域
  10. [FATAL] [DBT-10317] Specified SID Name (lhr122) already exists.
  11. 5.1 Zend_Log_Writer
  12. aac蓝牙编解码协议_蓝牙协议总结
  13. 读书笔记之语法语料库和语义知识库
  14. win7 uefi 无法对计算机,U盘UEFI模式无法启动WIN7安装解决教程
  15. 怎么把记事本内容导出python_怎么把记事本内容导出python
  16. 第八章-数据处理的两个基本问题
  17. android emoji转换成字符串,安卓的emoji表情转换为文字
  18. android 强制退出程序,Android 强制且彻底的退出app
  19. 元数据管理器中存在错误
  20. 什么叫模拟量和数字量

热门文章

  1. 数学复习全程规划及方法指导(武忠祥笔记)
  2. C语言中scanf与分隔符(空格回车Tab)
  3. [PHP] PHP7.4.2安全和修复版本的更改日志
  4. 10-8 猫和狗 : 创建两个文件cats.txt和dogs.txt, 在第一个文件中至少存储三只猫的名字, 在第二个文件中至少存储三条狗的名字。 编写一个程序, 尝试读取这些文件, 并将其内容打印到
  5. 计算机二级C语言-A程序填空题
  6. 攻防演练 第三方供应商梳理
  7. 单招考计算机一般考什么科目,单招一般都考什么科目 单招的考试范围是什么...
  8. 程序员需要的日常(收费)软件(都免费)
  9. 说说filter这个css属性(网站置灰实现)
  10. 齿轮画板Python小游戏(附源码)