一文带你入门WebSocket(一文带你入门图论和网络分析)

WebSocket前世
众所周知,Web应用的交互过程通常是客户端通过浏览器发送请求,服务器接收请求,处理后返回结果给客户端,客户端的浏览器呈现信息。这种机制对于信息变化不频繁的应用来说是可以接受的,但是对于实时性要求高、海量并发的应用来说就显得捉襟见肘了,尤其是在当前行业内移动互联网蓬勃发展的趋势下。高并发和用户的实时响应是Web应用面临的普遍问题,如金融证券的实时信息、Web导航应用中的地理位置获取、社交网络中的实时消息推送等。
传统请求-响应模式的Web开发在处理此类业务场景时通常采用实时通信方案,常见的有:
轮询的原理简单易懂,就是客户端在一定的时间间隔内通过频繁的请求向服务器发送请求,以保持客户端和服务器之间的数据同步。问题很明显。当客户端以固定的频率向服务器发送请求时,服务器的数据可能不会更新,带来很多不必要的请求,浪费带宽,效率低下。AdobeFlash基于Flash,通过自己的Socket完成数据交换,然后使用Flash将相应的接口暴露为JavaScript调用,从而达到实时传输的目的。这种方式比轮询效率高,而且由于Flash的安装率高,应用场景广,所以Flash在移动互联网终端上的支持并不好。IOS系统没有Flash,虽然Android支持,但实际使用效果并不理想,需要移动设备的硬件配置。2012年,Adobe正式宣布不再支持Android4.1系统,宣告了Flash在移动终端上的死亡。从上面可以看出,传统的Web模式在处理高并发性和实时性要求时会遇到不可逾越的瓶颈。我们需要一种高效节能的双向通信机制来保证数据的实时传输。在此背景下,基于HTML5规范,被称为Web TCP的WebSocket应运而生。
HTML5早期在业界没有形成统一的标准,各个浏览器和应用服务器厂商都有不同的类似实现,比如IBM的MQTT和Comet开源框架。直到2014年,HTML5在IBM、微软、Google等巨头的推动和协作下终于尘埃落定,从草案正式实施为实际的标准规范。各个应用服务器和浏览器厂商逐渐开始统一,WebSocket协议也在JavaEE7中实现,这样客户端和服务器WebSockets都已经完成。读者可以参考HTML5规范,熟悉新的HTML协议规范和WebSocket支持。
WebSocket机制
下面简单介绍一下WebSocket的原理和运行机制。
WebSocket是HTML5的一个新协议。实现了浏览器与服务器的全双工通信,可以更好的节省服务器资源和带宽,实现实时通信。它是基于TCP的,和HTTP一样通过TCP传输数据,但是它和HTTP最大的区别是:
WebSocket是一种双向通信协议。连接建立后,WebSocket服务器和浏览器/客户端代理可以像Socket一样主动地互相发送或接收数据。WebSocket需要类似TCP的客户端和服务器通过握手的方式进行连接,连接成功后才能相互通信。非WebSocket模式下传统HTTP客户端与服务器的交互如下图所示:
图一。传统HTTP请求响应的客户端-服务器交互图
使用WebSocket模式的客户端和服务器之间的交互如下:
图二。2的客户端-服务器交互图。WebSocket请求响应
从上图对比可以看出,与传统的每次请求-响应都需要客户端与服务器建立连接的HTTP模式相比,WebSocket是一种类似Socket的TCP长连接通信模式。WebSocket连接一旦建立,后续数据将以帧序列的形式传输。在客户端断开WebSocket连接或者服务器断开连接之前,客户端和服务器不需要重新发起连接请求。在客户端和服务器端大并发、大交互负载流量的情况下,大大节省了网络带宽资源的消耗,性能优势明显。此外,客户端在同一个持久连接上发送和接收消息,因此实时优势显而易见。
让我们通过客户端和服务器之间的交互消息来看看WebSocket通信和传统HTTP的区别:
在客户端,new WebSocket实例化一个新的WebSocket客户端对象,连接类似ws 3360//your domain : port/path的服务器端的WebSocket URL。WebSocket客户端对象将被自动解析并识别为WebSocket请求,从而连接服务器端口并执行双方之间的握手过程。客户端发送的数据格式类似:
1.WebSocket客户端连接消息
GET /webfin/websocket/HTTP/1.1
主机:本地主机
升级: websocket
连接:升级
sec-web socket-key : xqbt 3 imnzjbyqrinxeflkg==
Origin:本地主机:8080
Sec-WebSocket-Version: 13
可以看出,客户端发起的WebSocket连接消息类似于传统的HTTP消息。“Upgrade:websocket”参数值表示是websocket类型请求,“Sec-WebSocket-Key”是WebSocket客户端发送的base64编码密文。要求服务器必须返回相应的加密“Sec-WebSocket-Accept”响应,否则客户端会抛出“WebSocket握手过程中出错”错误并关闭连接。
服务器收到消息后返回的数据格式类似:
2.WebSocket服务器响应消息
HTTP/1.1 101交换协议
升级: websocket
连接:升级
sec-web socket-accept :k 7 djldlooiwig/mopvwfb 3y 3 Fe 8=
“Sec-WebSocket-Accept”的值由服务器用与客户端相同的密钥计算并返回给客户端。“HTTP/1.1 101交换协议”是指服务器接受WebSocket协议的客户端连接。经过这样的请求-响应处理,客户端-服务器WebSocket连接握手成功,后续可以进行TCP通信。读者可以参考WebSocket协议栈,了解WebSocket客户端和服务器之间更详细的交互数据格式。
在开发方面,WebSocket API也非常简单。我们只需要实例化WebSocket并创建一个连接,然后服务器和客户端就可以互相发送和响应消息了。在下面的WebSocket实现和案例分析部分,我们可以看到详细的WebSocket API和代码实现。
WebSocket实现
如上所述,WebSocket的实现分为两部分:客户端和服务器端。客户端(通常是浏览器)发出WebSocket连接请求,服务器响应,实现一个类似TCP握手的动作,从而在浏览器客户端和WebSocket服务器之间形成一个HTTP长连接快速通道。在它们之间的直接数据传输之后,不需要发起连接和通信。
下面简单介绍一下WebSocket服务器API和客户端API。
WebSocket服务器API
WebSocket服务器在所有主流应用服务器厂商中基本都已经得到了JEE JSR356 API的支持(详见JSR356 WebSocket API规范)。下面是一些常见的支持WebSocket server的商业和开源应用服务器:
表1。WebSocket服务器支持
厂商应用服务器备注支持IBM WebSphere WebSphere及以上版本;7之前的版本。x支持与MQTT类似的HTTP长连接;11g和10g版本通过HTTP Publish支持类似的HTTP长连接;微软IIS 7.0支持Apache
TomcatTomcat 7.0.5+支持,7.0.2X和7.0.3X支持JettyJetty 7.0+支持通过自定义API。
我们用Tomcat7.0.5版本7.0.5服务器示例代码来说明WebSocket服务器的实现:
JSR356的WebSocket规范使用javax.websocket.*的API,一个通用的Java对象(POJO)可以使用@ServerEndpoint注释作为websocket服务器的端点。代码示例如下:
清单3。WebSocket服务器API示例
@ServerEndpoint(\’/echo\ ‘)
公共类EchoEndpoint {
@OnOpen
公共void onOpen(会话Session)引发IOException {
//下面的代码被省略.
}
@OnMessage
公共字符串onMessage(字符串消息){
//下面的代码被省略.
}
@Message(maxMessageSize=6)
public void receiveMessage(字符串){
//下面的代码被省略.
}
@OnError
public void onError(Throwable t) {
//下面的代码被省略.
}
@OnClose
public void on close(Session Session,CloseReason reason) {
//下面的代码被省略.
}
}
代码解释:
上面简洁的代码建立了一个WebSocket服务器,@ Server endpoint(\ ‘/echo \ The annotation endpoint of ‘)表示WebSocket服务器运行在ws :/[服务器IP或域名]:[服务器端口]/websockets/echo的访问端点,客户端浏览器已经可以向WebSocket客户端API发起HTTP长连接,
用ServerEndpoint标注的类必须有一个公共的无参数构造函数,用@onMessage标注的Java方法用于接收传入的WebSocket信息,可以是文本格式,也可以是二进制格式。
当在此端点建立新连接时,将调用OnOpen。参数提供了连接另一端的更多详细信息。Session表示两个WebSocket端点之间对话连接的另一端,可以理解为类似于HTTPSession的概念。
当连接终止时,调用OnClose。参数closeReason可以封装更多的细节,比如WebSocket连接关闭的原因。
更高级的定制如@Message comment、MaxMessageSize属性可以用来定义消息字节的最大限制。在示例程序中,如果收到的信息超过6个字节,就会报告错误并关闭连接。
注意:早期不同的应用服务器支持不同的WebSocket方式,即使出自同一家厂商,不同版本也有细微差别。比如7.0.5以上的Tomcat服务器版本都是在标准的JSR356规范中实现的,而7.0.2x/7.0.3X版本使用了自定义API (WebSocketServlet和StreamInbound,前者是初始化WebSocket环境的容器;后者用于具体处理WebSocket请求和响应,详见案例分析部分),而Tomcat7.0.3x与7.0.2x的createWebSocketInbound方法定义不同,增加了一个HttpServletRequest参数,这样就可以从Request参数中获得更多关于WebSocket客户端的信息,如下面的代码所示:
清单4。Tomcat版本7.0.3x的WebSocket API
公共类EchoServlet扩展WebSocketServlet {
@覆盖
受保护的stream inbound createWebSocketInbound(字符串子协议,
HttpServletRequest请求){
//下面的代码被省略.
返回新消息Inbound() {
//下面的代码被省略.
}
受保护的void onBinaryMessage(ByteBuffer缓冲区)
引发IOException {
//下面的代码被省略.
}
受保护的void ontext message(char buffer buffer)引发IOException {
getWsOutbound()。writeTextMessage(缓冲区);
//下面的代码被省略.
}
};
}
}
因此,WebSocket的服务器端需要选择它的版本。通常,新版本支持WebSocket的标准JSR API,但也需要考虑旧版本程序的易开发性和可移植性等问题,比如下面介绍的客户案例。它是Tomcat版本7.0.3x的WebSocketServlet实现,使用它是因为客户需要统一的应用服务器版本,而不是JSR356的@ServerEndpoint注释端点。
WebSocket客户端API
对于WebSocket客户端,现在主流浏览器(包括PC和移动终端)都支持标准的HTML5 WebSocket API,这意味着客户端的WebSocket JavaScirpt脚本具有良好的一致性和跨平台特性。下面列出了常见浏览器厂商对WebSocket的支持:
表二。WebSocket客户端支持
浏览器支持ChromeChrome版本4支持FirefoxFirefox版本5支持IEIE版本10支持SafariIOS 5支持Android浏览器droid 4.5支持
客户端WebSocket API在主流浏览器厂商中已经基本统一,所以使用标准HTML5定义的WebSocket客户端的JavaScript API就足够了,当然也可以使用业界符合WebSocket标准规范的开源框架,比如Socket.io
下面的代码示例阐释了WebSocket的客户端实现:
清单5。5的例子。WebSocket客户端API
var ws=new web socket(” ws ://echo . web socket . org “);
ws . on open=function(){ ws . send(” Test!”);};
ws . on message=function(evt){ console . log(evt . data);ws . close();};
ws . on close=function(evt){ console . log(” WebSocket closed!”);};
ws . on error=function(evt){ console . log(” WebSocketError!”);};
第一行代码是申请一个WebSocket对象,参数是要连接的服务器的地址。和HTTP协议开头一样,WebSocket协议的URL以wss://开头,安全WebSocket协议以WSS3360//开头。
第二行到第五行注册WebSocket对象的消息处理函数。WebSocket对象支持四种消息:onopen、onmessage、onclose和onerror。有了这四个事件,我们就可以轻松控制WebSocket了。
浏览器与WebSocketServer连接成功后,触发onopen消息;如果连接失败,发送和接收数据失败,或者处理数据出现错误,浏览器会触发onerror消息;当浏览器接收到WebSocketServer发送的数据时,会触发onmessage,参数evt包含服务器发送的数据;当浏览器接收到WebSocketServer发送的连接关闭请求时,会触发onclose消息。我们可以看到所有操作都是异步回调触发的,不会阻塞UI,可以获得更快的响应时间和更好的用户体验。
WebSocket案例分析
下面是一个真实的客户案例来分析说明WebSocket的优势及其具体的开发实现(为了保护客户的隐私,下面的描述中省略了客户的名字,与业务细节相关的代码在本文中不再重复)。
案例介绍
客户是一家移动设备制造商,移动设备安装了Android/IOS操作系统。设备分为两类(以下简称A、B),A类设备随时处于移动状态,B类设备是A类设备的管理和控制设备。客户需要随时看到B类设备中A类设备的地理位置信息和状态信息。比如A类设备在线或离线时,B类设备需要立即被通知,A类设备报告时,B类设备也需要实时获取被报告A类设备的地理位置信息。
为了降低跨平台的难度和实现的工作量,客户考虑轻量级的Web App来屏蔽Android/IOS平台之间的差异。A类设备数量较多,A类设备在工作状态下处于不规则运动状态,而B类设备对A类设备状态变化的实时感知要求较高(秒级)。
根据上述要求,A/B类设备的信息存储在后台数据库中,A/B类设备的交互涉及来自Web客户端/服务器的频繁且高并发的请求。如果采用传统的HTTP请求-响应模式,B类设备的Web App需要轮询服务,必然会给服务器带来很大的负载压力。而且,当A类设备不上线或者不上报其他活动事件时,B类设备的轮询会严重浪费网络资源。
解决办法
综上所述,本项目采用WebSocket技术实现实时消息的通知和推送。每当A类设备/B类设备在线登录成功,就会打开WebSocket的HTTP长连接。新A类设备在线、位置变更、离线等状态变化都会通过WebSocket发送实时消息。WebSocket服务器会对A类设备的实时消息进行处理,并实时推送给下属B类设备。
WebSocket客户端使用jQuery Mobile(本文不详细描述jQuery Mobile的开发,感兴趣的读者可以参考jQuery Mobile的介绍),使用原生的WebSocket API实现与服务器的交互。
服务端沿用客户已有的应用服务器Tomcat 7.0.33版本,使用街头流氓自定义应用程序界面实现WebSocket服务器端,为一个上线的英语字母表中第一个字母类设备生成一个WebSocket的超文本传送协议长连接,每当英语字母表中第一个字母类设备有上线,位置更新,离线等事件的时候,客户端发送文本消息,服务端识别并处理后,向所属仓库类设备发送实时消息,B类设备客户端接收消息后,识别到英语字母表中第一个字母类设备的相应事件,完成对应的英语字母表中第一个字母类设备位置刷新以及其他业务操作。
其涉及的英语字母表中第一个字母类设备,B类设备及后台服务器交互时序图如下:
图3:A/B类设备WebSocket交互图
A/B类设备的WebSocket客户端封装在websocket.js的Java脚本语言代码中,与jQuery MobileApp一同打包为移动端apk/ipa安装包;WebSocket服务端实现主要为WebSocketDeviceInbound.java,WebSocketDeviceInbound.java,WebSocketDeviceInbound池。爪哇几个类。下文我们一一介绍其具体代码实现。
代码实现
在下文中我们把本案例中的主要代码实现做解释说明,读者可以下载完整的代码清单做详细了解。
WebSocketDeviceServlet类
英语字母表中第一个字母类设备或者仓库类设备发起WebSocket长连接后,服务端接受请求的是WebSocketDeviceServlet类,跟传统HttpServlet不同的是,WebSocketDeviceServlet类实现createWebSocketInbound方法,类似实例的接受方法,新生产的WebSocketInbound实例对应客户端超文本传送协议长连接,处理与客户端交互功能。
WebSocketDeviceServlet服务端代码示例如下:
清单6.WebSocketDeviceServlet.java代码示例
公共类WebSocketDeviceServlet扩展org。阿帕奇。卡特琳娜。web套接字。websocketservlet {
private static final long serialVersionUID=1L;
@覆盖
受保护的流入站createWebSocketInbound(字符串子协议,HttpServletRequest请求){
WebSocketDeviceInbound newClientConn=new WebSocketDeviceInbound(request);
websocketdeviceinboundpool。addmessageinbound(new client conn);
返回newClientConn
}
}
代码解释:
WebSocketServlet是WebSocket协议的后台监听进程,和传统超文本传送协议请求一样,WebSocketServlet类似弹簧/结构中的小型应用程序监听进程,只不过通过客户端《华盛顿明星报》的前缀指定了其监听的协议为WebSocket。
WebSocketDeviceInboundPool实现了类似数据库编程数据库连接池的客户端WebSocket连接池功能,并统一处理WebSocket服务端对单个客户端/多个客户端(同组英语字母表中第一个字母类设备)的消息推送,详见WebSocketDeviceInboundPool代码类解释。
WebSocketDeviceInboundl类
WebSocketDeviceInbound类为每个英语字母表中第一个字母类和仓库类设备验证登录后,客户端建立的超文本传送协议长连接的对应后台服务类,类似窝编程中的实例接受后的窝进程,在WebSocketInbound中接收客户端发送的实时位置信息等消息,并向客户端(乙)类设备)发送下属英语字母表中第一个字母类设备实时位置信息及位置分析结果数据,输入流和输出流都是WebSocket协议定制的WsOutbound负责输出结果,流入站和WsInputStream负责接收数据:
清单7.WebSocketDeviceInbound.java类代码示例
公共类WebSocketDeviceInbound扩展邮件入站{
私有最终对象请求;
私人设备帐户已连接设备;
公共设备帐户getConnectedDevice() {
返回连接设备
}
public void setConnectedDevice(设备帐户连接的设备){
这个。连接的设备=连接的设备;
}
公共http servlet请求getRequest(){
退货请求;
}
公共WebSocketDeviceInbound(http servlet请求请求){
请求=请求
设备帐户连接da=(设备帐户)请求。getsession(true).获取属性(\ ‘连接的设备\ ‘);
if(connectedDa==null)
{
字符串设备id=请求。getparameter(\ ‘ id \ ‘);
DeviceAccountDao设备Dao=新设备account Dao();
连接的da=设备Dao。getdabyid(整数。parse int(设备id));
}
这个。setconnecteddevice(连接的da);
}
@覆盖
受保护的打开时无效(ws出站出站){
/
}
@覆盖
受保护的void onClose(int状态){
websocketdeviceinboundpool。removemessageinbound(this);
}
@覆盖
受保护的void二进制消息(字节缓冲消息)引发IOException {
抛出新的UnsupportedOperationException(\ ‘不支持二进制消息。\’);
}
@覆盖
受保护的void上下文消息(字符缓冲区消息)引发IOException {
websocketdeviceinboundpool。处理文本消息(这,消息。tostring());
}
公共void发送消息(BaseEvent事件)
{
string eventStr=JSON。tojsonstring(事件);
尝试{
this.getWsOutbound().编写文本消息(char缓冲区。wrap(eventStr));
//…以下代码省略
} catch (IOException e) {
e。printstacktrace();
}
}
}
代码解释:
连接设备是当前连接的A/B类客户端设备类实例,在这里做为成员变量以便后续处理交互。
发送消息函数向客户端发送数据,使用Websocket WsOutbound输出流向客户端推送数据,数据格式统一为JSON。
onTextMessage函数为客户端发送消息到服务器时触发事件,调用WebSocketDeviceInboundPool的processTextMessage统一处理英语字母表中第一个字母类设备的登入,更新位置,离线等消息。
事件函数触发关闭事件,在连接池中移除连接。
WebSocketDeviceInbound构造函数为客户端建立连接后,WebSocketServlet的createWebSocketInbound函数触发,查询英语字母表中第一个字母类/B类设备在后台数据库的详细数据并实例化连接设备做为WebSocketDeviceInbound的成员变量,WebSocketServlet类此时将新的WebSocketInbound实例加入自定义的WebSocketDeviceInboundPool连接池中,以便统一处理A/B设备组员关系及位置分布信息计算等业务逻辑。
WebSocketDeviceInboundPool类
WebSocketInboundPool类: 由于需要处理大量英语字母表中第一个字母类仓库类设备的实时消息,服务端会同时存在大量超文本传送协议长连接,为统一管理和有效利用超文本传送协议长连接资源,项目中使用了简单的模拟实现内存连接池机制,每次设备登入新建的WebSocketInbound都放入WebSocketInbound实例的连接池中,当设备登出时,从连接池中移动对应的WebSocketInbound实例。
此外,WebSocketInboundPool类还承担WebSocket客户端处理英语字母表中第一个字母类和仓库类设备间消息传递的作用,在客户端发送英语字母表中第一个字母类设备登入、登出及位置更新消息的时候,服务端WebSocketInboundPool进行位置分布信息的计算,并将计算完的结果向同时在线的仓库类设备推送。
清单8.WebSocketDeviceInboundPool.java代码示例
公共类WebSocketDeviceInboundPool {
私有静态最终数组列表连接数=
new ArrayList();
公共静态void addMessageInbound(WebSocketDeviceInbound inbound){
//添加连接
设备帐户da=入站。getconnecteddevice();
System.out.println(\ ‘新上线设备: \ ‘ da。getdevicenm());
connections.add(入站);
}
公共静态数组列表getOnlineDevices(){
ArrayList在线设备=new ArrayList();
用于(WebSocketDeviceInbound web客户端:连接)
{
在线设备。添加(webclient。getconnecteddevice());
}
返回在线设备
}
公共静态WebSocketDeviceInbound getGroupBDevices(字符串组){
WebSocketDeviceInbound ret webclient=null;
用于(WebSocketDeviceInbound web客户端:连接)
{
如果(webclient。getconnecteddevice().getDeviceGroup().等于(组)
webClient.getConnectedDevice().getType().等于(\ ‘ B \ ‘){
retWebClient=webClient
}
}
返回retWebClient
}
公共静态void removeMessageInbound(WebSocketDeviceInbound inbound){
//移除连接
System.out.println(\ ‘设备离线: \ ‘入境。getconnecteddevice());
连接.删除(入站);
}
公共静态void processTextMessage(WebSocketDeviceInbound入站,字符串消息){
基本事件接收事件=(基本事件)JSON。解析对象(消息。tostring()、基本事件。类);
DBEventHandleImpl dbEventHandle=new DBEventHandleImpl();
dbeventhandle。setreceiveevent(接收事件);
dbEventHandle .处理事件();
如果(接收事件。获取事件类型()==事件常数.EVENT_MATCHMATIC_RESULT||
接收事件。获取事件类型()==事件常数.事件组设备结果||
接收事件。获取事件类型()==事件常数.事件_ A _维修
string clientDeviceGroup=((ArrayList)
receiveEvent.getEventObjs()).获取(0)。getDeviceGroup();
WebSocketDeviceInbound b client=getGroupBDevices(clientDeviceGroup);
if(bClient!=null){
sendmessagestsingleclient(b客户端,dbeventhandle。getreceiveevent());
}
}
}
}
公共静态void sendMessageToAllDevices(基本事件事件){
尝试{
对于(WebSocketDeviceInbound web client :连接){
webClient.sendMessage(事件);
}
} catch(异常e) {
e。printstacktrace();
}
}
公共静态void sendmessagetsingleclient(WebSocketDeviceInbound web client,BaseEvent事件){
尝试{
webClient.sendMessage(事件);
}
捕捉(异常e) {
e。printstacktrace();
}
}
}
代码解释:
addMessageInbound函数向连接池中添加客户端建立好的连接。
getOnlineDevices函数获取所有的连线的A/B类设备。
removeMessageInbound函数实现英语字母表中第一个字母类设备或者仓库类设备离线退出(服务端收到客户端关闭WebSocket连接事件,触发WebSocketInbound中的事件方法),从连接池中删除连接设备客户端的连接实例。
processTextMessage完成处理客户端消息,这里使用了消息处理的机制,包括解码客户端消息,根据消息构造事件事件,通过事件处理句柄多线程处理,处理完后向客户端返回,可以向该组仓库设备推送消息,也可以向发送消息的客户端推送消息。
sendMessageToAllDevices函数实现发送数据给所有在线A/B类设备客户端sendMessageToSingleClient .函数实现向某一A/B类设备客户端发送数据。
websocket.js客户端代码
客户端代码websocket.js,客户端使用标准HTML5定义的WebSocket API,从而保证支持IE9,Chrome,FireFox等多种浏览器,并结合jQueryJS库应用程序界面处理数据数据的处理及发送。
清单9:客户端WebSocket.js脚本示例
var websocket=window .WebSocket ||窗口MozWebSocket。
var isConnected=false
函数doOpen(){
isConnected=true
if(deviceType==’B’){
mapArea=’ mapB
doLoginB(地图区域);
}
否则{
mapArea=’ mapA
多洛吉娜(地图区);
}
}
函数doClose(){
showDiagMsg(\’infoField\ ‘,\ ‘已经断开连接\ ‘,\ ‘信息对话框\ ‘);
isConnected=false
}
函数doError() {
showDiagMsg(\’infoField\ ‘,\ ‘连接异常!\ ‘,\ ‘信息对话框\ ‘);
isConnected=false
}
函数国内消息(消息){
定义变量事件=$。解析JSON(消息。数据);
doReciveEvent(事件);
}
函数多森(消息){
if (websocket!=null) {
web套接字。发送(JSON。stringify(消息));
}否则{
showDiagMsg(\’infoField\ ‘,\ ‘您已经掉线,无法与服务器通信!\ ‘,\ ‘信息对话框\ ‘);
}
}
//初始话WebSocket
函数initWebSocket(wcUrl) {
如果(窗口WebSocket) {
web socket=新的web socket(编码uri(wcUrl));
web套接字。on open=doOpen
web套接字。on错误=do错误;
web套接字。关闭时=单据关闭;
web套接字。论消息=家贤;
}
否则{
showDiagMsg(\’infoField\ ‘,\ ‘您的设备不支持webSocket!\ ‘,\ ‘信息对话框\ ‘);
}
};
函数doReciveEvent(事件){
//设备不存在,客户端断开连接
if(event.eventType==101){
showDiagMsg(\’infoField\ ‘,\ ‘设备不存在或设备号密码错!\ ‘,\ ‘信息对话框\ ‘);
web套接字。close();
}
//返回组设备及计算目标位置信息,更新地图
else if(事件。事件类型==104 | |事件。事件类型==103){
clearGMapOverlays(mapB);
$.each(event.eventObjs,function(idx,item){
var设备nm=项目。设备nm;
//谷歌应用编程接口
//var device locale=new Google。地图。锁定(项目。滞后,项目。lat);
//百度美国石油学会(American Petroleum Institute)
var deviceLocale=new BMap .点(item.lng,item。lat);
var newMarker
if(item.status==’target’){
newMarker=addMarkToMap(mapB,deviceLocale,deviceNm,true);
//…以下代码省略
}
否则{
newMarker=addMarkToMap(mapB,deviceLocale,device nm);
}
马卡雷。推(新标记);
});
showDiagMsg(\’infoField\ ‘,\ ‘有新报修设备或设备离线,地图已更新!\ ‘,\ ‘信息对话框\ ‘);
}
}
代码解释:
DoOpen回调函数处理WebSocket的打开。A类或B类设备连接到WebSocket服务器后,会初始化地图并显示默认位置,然后向服务器发送设备登录消息。
DoReciveEvent函数处理关闭WebSocket。当A类/B类设备离线时(退出移动终端上的应用),服务器关闭HTTP长连接,客户端WebSocket对象执行onclose回调句柄。
InitWebSocket初始化WebSocket,连接到WebSocket服务器,并设置句柄来处理回调。如果浏览器版本太低,不支持HTML5,它会提示客户端设备不支持WebSocket。
doSend函数处理客户端发送给服务器的消息。注意,消息是一个JSON OBJ对象,字符串是由JSON标准API格式化的。
doMessage函数处理WebSocket服务器返回的消息,后台返回的消息是一个JSON字符串,被jQuery的parseJSON API格式化为JSON对象,这样客户端在处理doReciveEvent函数时就可以接收到服务器返回的消息。因为涉及到很多业务逻辑,这里就不描述了。
结束语
以上简单介绍了WebSocket的由来、原理和机制,以及服务器/客户端的实现,并结合实际客户案例指导和讲解了如何使用WebSocket解决实时响应和服务器端消息推送的问题。本文适合熟悉HTML协议规范和J2EE Web编程的读者,旨在帮助读者快速熟悉HTML5 WebSocket的原理和开发应用。本文中的服务器和客户端项目代码可以下载,修改后可以用于用户基于WebSocket的HTTP长连接的实际生产环境中。

其他教程

声卡音效大全 音效库(声卡如何自定义音效)

2022-8-28 17:04:48

其他教程

联想2021拯救者r7000p和r9000p有什么区别(联想拯救者r7000p2020和2021)

2022-8-28 17:06:51

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索