丽枫,郑力新,王佳斌
(华侨大学 工学院,福建 泉州 362021)
摘要:随着互联网技术的不断发展,Web技术在各个领域得到了不同程度的运用,人们对于Web应用的实时性提出了更高的要求,HTML5WebSocket协议因此得到了广泛的关注。通过对基于HTTP的传统Web实时通信方案进行分析,针对其中的不足与缺点,深入介绍了基于HTML5 WebSocket协议的实时通信机制以及相对于传统方案的优势,并通过使用Node.js的Express框架和HTML5 WebSocket协议的第三方应用程序编程接口Socket.io类库实现了一个基于WebSocket协议的Web应用。经实验表明,所描述的研究能成功地在客户端和服务器端完成基于HTML5 WebSocket协议的实时通信过程并建立连接。
0引言
随着互联网技术的高速发展,人们对Web应用的实时性要求越来越高,传统的Web实时通信方案已经无法满足一些现实应用的需求。在长期的Web应用过程中该传统方案逐渐露出资源浪费、实时性不高等问题,这些问题的出现对一些实时性要求较高的Web应用(如在线游戏、在线证券、设备监控等)造成了不好的用户体验。除此之外,这些不足还会制约Web实时通信的性能,对通信效率造成影响。面对这种情况,HTML5规范中定义了WebSocket协议来实现更好的用户体验和实时通信功能,并针对传统的Web实时通信方案在实际运用中产生的资源浪费问题进行改善,提高通信效率。
目前,WebSocket协议的实现主要分为客户端和服务器端两部分。对于其客户端而言,许多的主流浏览器(包括个人电脑和移动终端)如谷歌、火狐、IE等都在不同的版本上支持WebSocket客户端应用程序编程接口。而对于其服务器端而言,也有许多常见的应用服务器如WebSphere、WebLogic、Tomcat等在不同的版本上支持WebSocket服务器端应用程序编程接口。综上所述,本文从传统的Web实时通信方案出发,针对其在Web应用中所体现的不足与缺点,深入研究WebSocket协议在Web实时通信方面的原理与优势,并根据该协议的通信机制进行实现。
1传统的Web实时通信方案
1.1轮询
在早期的Web应用中,所采用的Web实时通信方案是轮询。在使用轮询时,客户端需要频繁地向服务器端发送HTTP请求来保持客户端和服务器端的同步以便不断地刷新客户端所要呈现的信息。在这个过程中,客户端无法确定合适的时间间隔向服务器端发送HTTP请求。若间隔的时间太短,客户端频繁的请求将会给服务器端造成巨大的压力;若间隔的时间太长,就无法满足客户端和服务器端实时通信的要求。由于客户端在频繁地发送请求时服务器端的数据可能还未进行更新,导致服务器端返回的大部分应答包中的数据域为空,因而产生了很多无谓的网络传输,浪费了大量的带宽资源和其他网络资源。对于图1客户端与服务器端的交互图每次的HTTP请求而言,过长的HTTP头信息也会占用不必要的带宽资源。因此,这是一种缺乏灵活性又低效的Web实时通信方案。其中客户端和服务器端的交互过程如图1(a)所示。
1.2Comet技术
目前,Comet技术[1]的实现方式包括基于异步JavaScript和可扩展标记语言(Asynchronous JavaScript and Extensible Markup Language,AJAX)的长轮询方式和基于Iframe的流方式。这两种方式针对轮询都做出了较大的改进。
1.2.1基于AJAX的长轮询方式
基于AJAX的长轮询方式[2]通过采用AJAX技术让客户端向服务器端发送HTTP请求,进而与服务器端建立连接,且该连接会在服务器端保持一段时间。若服务器端检测到有新数据产生,那么它会将这些数据通过连接发送至客户端,然后关闭连接;若服务器端在连接存在期间都没有产生新的数据发送至客户端,那么它将会向客户端发送一个超时信息,然后关闭连接。无论服务器端的数据是否还在更新,在连接关闭之后,客户端都需要重新向服务器端发送HTTP请求来建立连接。其中客户端和服务器端的交互过程如图1(b)所示。
虽然这种方式能够对客户端的部分页面进行更新,减少服务器端发送的数据量,降低客户端请求的频率,减少无效的网络传输,但当服务器端更新数据的速度较快时,基于AJAX的长轮询方式将变成普通的轮询,不仅会降低其性能,而且还会对服务器端造成较大的处理压力。除此之外,为了保持HTTP连接长时间处于打开状态,服务器端也需要消耗一定的服务器资源。因此,使用基于AJAX的长轮询方式会产生资源浪费的问题。
1.2.2基于Iframe的流方式
基于Iframe的流方式[3]通过客户端页面上内嵌的一个Iframe标签向服务器端发送HTTP请求,服务器端在响应该请求后与客户端建立一条长连接。连接建立后,服务器端通过不断地更新该连接的状态以保持其不过期。当服务器端检测到有新数据产生时,它会将新数据通过该连接发送给客户端;当客户端和服务器端之间的通信出现问题导致连接出现错误或者关闭时,客户端会立即发出连接请求与服务器端重新建立连接,否则该连接会一直持续,不会关闭。其中客户端和服务器端的交互过程如图1(c)所示。
虽然这种方式有利于减少客户端的请求次数,减轻客户端和服务器端之间的网络负担,避免因频繁的建立连接和关闭连接所带来的资源浪费,但由于基于Iframe的流方式在连接过程中始终只维持一个长连接,因此客户端页面会一直处于加载过程中而无法显示页面加载完成,从而影响用户体验。且当有多个客户端同时向服务器端发送HTTP请求时,由于服务器端长期只维持一个连接,因此会导致服务器端在这种高并发状态下的处理能力降低,造成大量的服务器资源和其他网络资源被消耗。
由于基于AJAX的长轮询方式和基于Iframe的流方式在通信过程中一直采用HTTP作为通信协议,因此每次的HTTP请求和应答所携带的完整的HTTP头信息不仅增加了实时更新信息时的数据传输量,还造成带宽资源的浪费。此外,为了维持和协调通信过程中HTTP连接随时处于可用状态,服务器端也需要消耗资源。对于HTTP连接的建立和关闭过程而言,服务器端新产生的数据有可能会因为无法及时发送到客户端而导致客户端的数据丢失。由于这两种方式对Web应用中的实时信息和非实时信息的请求/响应方式都未发生改变,因此,当实时信息的请求较为频繁时,可能会造成服务器端较大的处理压力,从而影响非实时信息的呈现。其中基于HTTP的Web实时应用模型如图2所示。
2传统的Web实时通信方案
WebSocket协议[45]是HTML5规范中的一种新的通信协议,是能够在客户端和服务器端进行异步通信的一种方法。它支持客户端与服务器端通过全双工通信的方式实现实时通信,本质上是一个基于传输控制协议的协议。因此,WebSocket连接的建立过程与传输控制协议连接的建立过程有些相似,客户端和服务器端需要通过“握手”来建立WebSocket连接。
首先由客户端向服务器端发送一个HTTP请求,该请求不同于一般的HTTP请求,它包含了一些附加的HTTP头信息,其中一条信息“Upgrade:WebSocket”表明这是一个申请将当前HTTP协议升级为WebSocket协议的HTTP请求。若服务器端收到该请求后能正确解读其HTTP头信息,那么它会返回一个基于HTTP的应答报文给客户端,此时连接建立成功[6],之后,客户端和服务器端便可以通过该连接主动向对方发送或者接收数据,直到其中一方主动关闭该连接。其中客户端和服务器端的交互过程如图3所示。
通过WebSocket协议,客户端和服务器端之间只要做一个“握手”的动作就可以建立一条双向通信的通道。这不仅让服务器端可以主动与客户端互发信息,而且还避免了因客户端频繁请求而造成的网络资源浪费、实时通信效率低、服务器处理压力大等问题[7]。由于WebSocket连接采用WebSocket协议作为通信协议,因此在传输过程中数据帧的头部信息所占的字节数将大大降低,从而有效地减小了通信过程中传输的数据量和网络负载,节约了带宽资源。在基于WebSocket协议的实时通信方案中,Web应用中的实时部分和非实时部分被加以区分。客户端使用WebSocket协议获取实时内容,使用HTTP协议获取非实时内容。而服务器端则采用两种不同的模块来处理实时的WebSocket请求和非实时的HTTP请求,其应用模型如图4所示。
通过上述模型可以看出,该实时通信方案使服务器端的结构更加明确,不仅让WebSocket协议和HTTP协议各司其职、互不干扰,而且还降低了系统的耦合性,在最大程度上发挥了两个模块的功能。此外,由于采用以传输控制协议为基础的WebSocket协议来处理实时服务,因此可以保证传输数据过程中的稳定性和及时性,在较大程度上提高了实时通信的性能。相对于传统方案来说,该方案不仅减小了对服务器资源的浪费,也减轻了服务器端的处理压力。
3基于WebSocket的Web实时通信应用实例
本文采用基于Node.js[8]的Express框架和Socket.io类库来实现基于WebSocket的Web实时通信应用。其中,Node.js是一个JavaScript运行平台,可用于构建响应速度快、容易扩展的网络程序。但由于Node.js中只提供了大量的低端功能,因此文中将使用Express框架进行Web实时通信应用的开发。Express是一个能够在Node.js中使用的 Web应用程序开发框架,它提供的一系列强大的特性,能够让Web应用程序的开发变得更加方便、快速。
Socket.io是一个开源、跨平台且支持客户端和服务器端进行实时双向通信的WebSocket库[9-10]。它包括客户端的JavaScript库和服务器端的Node.js模块。它能够根据不同的客户端自动在一些实时通信机制中选择合适的一个来实现Web实时应用。当使用支持HTML5技术的浏览器客户端进行实时通信时,Socket.io会选译效率最高、消耗服务器资源最少的WebSocket协议来实现实时通信,并在浏览器客户端发生变化时自动选择其他方式进行通信。因此,Socket.io能有效解决跨平台的实时通信问题。
3.1在线聊天室的设计
在线聊天室的设计分为客户端与服务器端两个部分,其实时通信过程如图5所示。
3.2在线聊天室的实现
在线聊天室的实现也分为客户端和服务器端两个部分。其中客户端通过使用HTML5、层叠样式表以及JavaScript来实现用户名的验证功能、消息显示功能和数据传送功能。服务器端通过JavaScript来实现与客户端的实时通信功能、广播功能以及在线用户列表的管理功能。图8用户登录成功时客户端与服务器端的交互图3.2.1客户端的实现过程
当有新的客户端用户加入聊天室时,已在聊天室的用户将会接收到新用户加入聊天室的消息且用户列表会被即时更新以显示新加入的用户名。新用户所在页面也会被更新以显示所有在线用户。当有客户端用户在聊天室发送聊天消息时,该消息会被即时广播给所有在线用户。当有客户端用户退出聊天室时,其他在线用户将会接收到该用户退出聊天室的消息且用户列表会被实时更新以移除下线用户的用户名。下线用户所在的页面也会进行相应的调整。若用户在聊天过程中直接退出聊天室页面,则所有在线用户都会收到该用户退出聊天室的消息。客户端的具体实现流程如图6所示。
3.2.2服务器端的实现过程
当有多个客户端用户存在时,服务器端的主要功能包括管理所有在线用户的用户列表以及广播它们之间的聊天消息。服务器端的具体实现流程如图7所示。
3.2.3客户端和服务器端的交互过程
本文主要针对用户成功登录进聊天室的情况进行介绍。当用户成功登录在线聊天室时,客户端和服务器端通过触发事件进行实时交互,其具体交互过程如图8所示。
4结论
传统的Web实时通信方案是在长期的应用实践中发展出来的,其中比较常用的是基于AJAX的长轮询方式和基于Iframe的流方式。但由于这两种方案都是采用基于HTTP的通信方式,因此当Web实时应用采用这两种方案时会产生难以解决的问题。而WebSocket协议的出现适时地提供了一种新的Web实时通信方案,它能够更加快捷有效地构建出简单高效的Web实时应用。因此,本文通过分析传统的Web实时通信方案的不足之处,不仅从理论层面分析了基于WebSocket的Web实时通信方案的优势,而且还通过使用HTML5、层叠样式表和JavaScript编写了具体的应用实例简单的实现了该方案。随着WebSocket协议的不断发展,基于WebSocket的Web实时通信方案将会被广泛应用。
参考文献
[1] 蔡骥然,曹海传.B/S架构下基于OPC与Comet技术的实时监控系统[J].计算机应用,2012,32(z2):214216.
[2] 文爱平,文德民.基于IE浏览器的Ajax Comet架构[J].电脑知识与技术,2010,6(17):46464648.
[3] 张家爱,孙飞.Comet技术在Web开发中的研究与应用[J].煤炭技术,2011,30(12):153154.
[4] 陆晨,冯向阳,苏厚勤.HTML5 WebSocket握手协议的研究与实现[J].计算机应用与软件,2015,32(1):128131,178.
[5] 李代立,陈榕.WebSocket在Web实时通信领域的研究[J].电脑知识与技术,2010,6(28):79237925,7935.
[6] 周东仿,孟宁.基于WebSocket的网络设备自发现机制[J].计算机工程与设计,2013,34(2):392396,438.
[7] 温照松,易仁伟,姚寒冰.基于WebSocket的实时Web应用解决方案[J].电脑知识与技术,2012,8(16):38263828.
[8] 王金龙,宋斌,丁锐.Node.js:一种新的Web应用构建技术[J].现代电子技术,2015,38(6):7073.
[9] 李广文.基于Socket.io的互动教学即时反馈系统的设计与实现[J].中国现代教育装备,2012(18):1012.
[10] 黄经赢.基于Socket.io+Node.js+Redis构建高效即时通讯系统[J].现代计算机(专业版),2014(19):6264,69.
随着ChatGPT的热度不断提升,web即时通讯也再一次回到到大众的眼中。 那种对话式的交互方式,以及自回归的文本展现模式,给人一种更加亲切、更加流畅的交流体验。
在此总结一下web即时通讯中几种常见的实现方式。
那这种交互方式,该如何实现呢? 在SSE之前,我们一般有如下几种实现方式,Ajax轮询、Comet和websocket;
Ajax轮询是指在客户端通过JavaScript定时发送HTTP请求到服务器,服务器在获取到新数据时返回给客户端。
setInterval(function() {
// 发送Ajax请求
$.ajax({
url: 'your_server_url',
method: 'GET',
success: function(response) {
// 处理响应数据
},
error: function(xhr, status, error) {
// 处理请求错误
}
});
}, interval_time);success: function(response) {
// 处理响应数据
// ...
// 重新发起下一次Ajax请求
}这种方式的缺点在于每次请求都是一次完整的HTTP连接,频繁的HTTP请求会带来较大的网络负载;同时服务器端产生的数据变化也不能及时的发送到客户端,而是需要等到下次请求发出时才能被获取,为了能够让客户端即时获取服务端的数据变化,就需要提高轮询的频率;
而连接的创建和释放在完整的一次HTTP请求中,占用了相当的比例,当轮询频率增加时,创建和释放连接消耗的时间相当可观,为了解决这一问题,就有了Comet。
Comet使用了一种称为"服务器推送"的方法,通过建立长期的HTTP连接来实现从服务器到客户端的数据推送。Comet的实现主要有两种方式,基于Ajax的长轮询(long-polling)方式和基于 Iframe 及 htmlfile 的流(http streaming)方式。
function longPolling() {
$.ajax({
url: 'your_server_url',
method: 'GET',
timeout: 30000, // 设置超时时间
success: function(response) {
// 处理服务器返回的数据
// ...
// 再次发起长轮询请求
longPolling();
},
error: function(xhr, status, error) {
// 处理请求错误
// ...
// 再次发起长轮询请求
longPolling();
}
});
}
// 发起初始请求
longPolling();通过Ajax的长轮询方式实现的即时通讯可以实现实时通讯的效果。但它也有一些缺点,如需要频繁地发起请求和保持连接,服务器资源开销较大。因此,在大规模的实时应用中,更推荐使用更高效的技术,如WebSocket或HTML5的Server-Sent Events。这些技术可以更好地支持实时通信,并减少不必要的网络请求。
var iframe=document.createElement('iframe');
iframe.style.display='none';
iframe.src='your_server_url';
document.body.appendChild(iframe);<!DOCTYPE html>
<html>
<head>
<title>Server Sent Events</title>
</head>
<body>
<script>
// 将即时数据作为HTML的内容输出
document.write('your_instant_data');
</script>
</body>
</html>iframe.onload=function() {
// 处理服务器返回的数据
var response=iframe.contentDocument.body.innerHTML;
// ...
// 重新发起请求以接收下一次的即时数据
iframe.src='your_server_url';
};通过基于iframe和htmlfile的流方式实现的即时通讯可以实现实时通讯的效果。服务器端每次有新的数据时,会将数据以HTML的形式返回给客户端,客户端通过监听iframe的onload事件来获取并处理数据。这种方式相对于长轮询的方式来说,可以减少请求的次数,减轻服务器的负担。但是它仍然存在一些缺点,如需要频繁地创建和销毁iframe元素,以及一些浏览器兼容性的问题。因此,在实际应用中,更推荐使用更高效和更现代的技术,如WebSocket或HTML5的Server-Sent Events来实现即时通讯。
var socket=new WebSocket('ws://your_server_url');socket.onopen=function() {
// WebSocket连接成功
// 可以在此处发送一些初始化的消息给服务器端
};
socket.onmessage=function(event) {
// 接收到服务器端发送的消息
var message=event.data;
// 处理接收到的消息
};
socket.onclose=function() {
// WebSocket连接关闭
};socket.send('your_message');// 引入WebSocket模块(Node.js)
const WebSocket=require('ws');
// 创建WebSocket服务器
const wss=new WebSocket.Server({ port: 8080 });
// 监听WebSocket的连接事件
wss.on('connection', function(ws) {
// 监听WebSocket的消息事件
ws.on('message', function(message) {
// 处理接收到的消息
});
// 监听WebSocket的关闭事件
ws.on('close', function() {
// 处理连接关闭
});
});通过WebSocket实现的即时通讯可以实现实时双向的通信效果,而且相比于长轮询和流方式,WebSocket具有更低的延迟和更高的性能。同时,WebSocket还支持跨域通信和加密等功能,使得它成为了实时通讯的首选技术。在实际应用中,可以使用现成的WebSocket库,如Socket.io来简化开发。
使用WebSocket实现Web即时通讯的确有一些缺点,包括:
尽管WebSocket存在一些缺点,但它仍然是目前实现Web即时通讯的首选技术,由于其高性能和实时性,广泛应用于聊天应用程序、协作工具、多人游戏和实时数据传输等场景。
服务器推送事件(Server-sent Events),简称SSE,是 HTML 5 规范中的一个组成部分,可以用来从服务端实时推送数据到浏览器端。相对于与之类似的 COMET 和 WebSocket 技术来说,服务器推送事件的使用更简单,对服务器端的改动也比较小。对于某些类型的应用来说,服务器推送事件是最佳的选择。
var eventSource=new EventSource('your_server_url');eventSource.onmessage=function(event) {
// 接收到服务器端发送的消息
var message=event.data;
// 处理接收到的消息
};
eventSource.onerror=function(event) {
// 发生错误
};
eventSource.onclose=function(event) {
// 连接关闭
};// 设置Content-Type为text/event-stream
res.setHeader('Content-Type', 'text/event-stream');
// 设置响应头,指定连接时长和允许跨域等信息
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('Access-Control-Allow-Origin', '*');
// 发送消息给客户端
res.write('data: ' + message + '\n\n');通过SSE实现的即时通讯是基于HTTP长连接的,与WebSocket相比,SSE更加简单易用,并且不需要额外的协议和握手过程。然而,相对于WebSocket,SSE在性能和实时性方面可能稍逊一筹,并且不支持双向通信。因此,在选择使用SSE还是WebSocket时,需要根据具体的应用场景和需求来决定。
ventSource 是 HTML5 新增的特性,用于实现服务器端数据实时推送到客户端。
使用 EventSource 可以这样实现服务端数据实时推送:
服务器:
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class StreamServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
PrintWriter writer=response.getWriter();
writer.write("data: some data\n\n");
writer.flush();
}
}客户端:
<script>
var source=new EventSource('/stream');
source.onmessage=function(e) {
console.log(e.data);
};
</script>然后在客户端使用 EventSource 连接到 http://yourserver/stream
工作原理:
这里的服务端使用了 Servlet 实现数据推送,reactor 模式下,每次调用 writer.write() 都会触发响应被刷新,实现了数据的实时推送。
EventSource 特性兼容 IE10 以上浏览器,其他浏览器也都有较好的支持,所以在实际场景中比较方便使用。希望这个示例能帮你明白 EventSource 的工作原理和基本使用方法。
*请认真填写需求信息,我们会在24小时内与您取得联系。