当前位置: 首页 > 后端技术 > Node.js

快速实现一个基于socket.io的实时通讯应用

时间:2023-04-03 12:25:59 Node.js

随着Web技术的发展,使用场景和需求也越来越复杂。客户不再满足于获取状态的简单请求。实时通信越来越多地应用于各个领域。HTTP是客户端和服务器端最常用的通信技术,但是HTTP通信只能由客户端发起,不能及时获取服务器端的数据变化。您只能依靠定期轮询来获取最新状态。时效性无法保证,请求多了会增加服务器的负担。WebSocket技术应运而生。WebSocket的概念不同于HTTP半双工协议。WebSocket是一种基于TCP连接的全双工协议,支持客户端和服务器端的双向通信。WebSocket使得客户端和服务器之间的数据交换更加容易,允许服务器主动向客户端推送数据。在WebSocketAPI中,浏览器和服务器只需要完成一次握手,两者之间就可以直接建立持久连接,进行双向数据传输。在WebSocketAPI中,浏览器和服务器只需要进行一次握手,浏览器和服务器之间就形成了一个快速通道。两者之间可以直接传输数据。实现WebSocket对象的原生实现,一共支持四种消息onopen、onmessage、onclose和onerror。建立连接你可以通过javascript快速建立一个WebSocket连接:varSocket=newWebSocket(url,[protocol]);上面代码中的第一个参数url指定了连接的URL。第二个参数协议是可选的,指定可接受的子协议。和HTTP协议以http://开头一样,WebSocket协议的URL以ws://开头,安全的WebSocket协议以wss://开头。当Browser和WebSocketServer连接成功后,会触发onopen消息。Socket.onopen=function(evt){};如果连接失败,发送或接收数据失败,或者处理数据有错误,浏览器会触发onerror消息。Socket.onerror=function(evt){};当Browser收到WebSocketServer发送的关闭连接的请求时,会触发onclose消息。Socket.onclose=function(evt){};发送和接收消息当Browser收到WebSocketServer发送的数据时,会触发onmessage消息,参数evt包含了服务器发送的数据。Socket.onmessage=function(evt){};send用于向服务器发送消息。Socket.send();socketWebSocket是随着HTML5提出来的,所以存在兼容性问题。这时,一个非常有用的库登场了——Socket.io。Socket.io封装了websocket并包含了其他的连接方法。您可以使用socket.io在任何浏览器中建立异步连接。Socket.io包括用于服务器和客户端的库。如果在浏览器中使用socket.iojs,则服务器端也必须适用。socket.io是一个基于Websocket的Client-Server实时通信库。socket.io的底层是基于engine.io库的。engine.io为socket.io提供了跨浏览器/跨设备双向通信的底层库。engine.io使用Websocket和XHR封装了一套socket协议。在低版本浏览器中,不支持Websocket,为了兼容,改用长轮询(polling)。API文档Socket.io允许你触发或响应自定义事件,除了connect、message、disconnect不能使用的事件名称外,你可以触发任何自定义事件名称。建立连接constsocket=io("ws://0.0.0.0:port");//port是自己定义的端口号letio=require("socket.io")(http);io.on("connection",function(socket){})消息收发1.发送数据socket.emit(customsentfield,data);2.接收数据socket.on(customsendfield,function(data){console.log(data);})Disconnect1.断开所有连接letio=require("socket.io")(http);io.close();2.客户端断开与服务器的连接//Clientsocket.emit("close",{});//服务器socket.on("close",data=>{socket.disconnect(true);});room和namespace有时会有以下使用websocket的场景:1.服务端发送的消息是分类的,不同的客户端需要接收不同的分类;2、服务端不需要向所有客户端发送消息,只需要向特定的组发送消息;对于这种使用场景,socket中非常实用的namespace和room就派上用场了。放一张图看看namespace和room的关系:是后命名空间`});});io.of("/get").on("connection",function(socket){socket.emit("newmessage",{mess:`Thisisthenamespaceofget`});});客户端//index.jsconstsocket=io("ws://0.0.0.0:****/post");socket.on("newmessage",function(data){console.log('index',data);}//message.jsconstsocket=io("ws://0.0.0.0:****/get");socket.on("newmessage",function(data){console.log('message',data);}roomclient//可用于client进入房间;socket.join('roomone');//用于离开房间;socket.leave('roomone');serverio.sockets.on('connection',function(socket){//提交者将被排除(即不会收到消息)socket.broadcast.to('roomone').emit('newmessages',data);//向所有用户发送消息io.sockets.to(data).emit("recivemessage","你好,房间里的用户");}使用socket.io实现一个实时接收信息的例子终于到了应用、服务阶段服务器接口是用node.js模拟的。下面的例子都是在本地服务器实现的。我们先来看看服务器。我们先启动一个服务,安装express和socket.io安装依赖npminstall--Devexpressnpminstall--Devsocket.iobuildnodeserverletapp=require("express")();让http=require("http").createServer(handler);让io=require("socket.io")(http);让fs=require("fs");http.listen(端口);//port:输入需要的端口号functionhandler(req,res){fs.readFile(__dirname+"/index.html",function(err,data){if(err){res.writeHead(500);returnres.end("加载index.html时出错");}res.writeHead(200);res.end(data);});}io.on("connection",function(socket){console.log('ConnectionSuccess');//连接成功后发送消息socket.emit("newmessage",{mess:`Initialmessage`});});客户端核心代码——index.html(向服务器发送数据)

发送消息
发送//从服务器接收一条匹配名字的消息socket.on("newmessage",function(data){console.log(data);});functionpostMessage(){socket.emit("recivemessage",{message:content,time:newDate()});messList.push({message:content,time:newDate()});}核心代码——message.html(从服务器接收数据)socket.on("newmessage",function(data){console.log(data);});Effect实时通信效果所有客户端断开连接一个客户端断开连接('http://localhost:port');componentDidMount(){socket.on('login',(data)=>{console.log(data)});socket.on('adduser',(data)=>{console.log(data)});socket.on('newmessage',(data)=>{console.log(data)});}解析webSocket协议Headers请求包Accept-Encoding:gzip,deflateAccept-Language:zh-CN,zh;q=0.9,en;q=0.8缓存控制:无缓存连接:升级Cookie:MEIQIA_VISIT_ID=1IcBRlE1mZhdVi1dEFNtGNAfjyG;令牌=0b81ffd758ea4a33e7724d9c67efbb26;io=ouI5Vqe7_WnIHlKnAAAG主机:0.0.0.0:2699来源:http://127.0.0.1:5500Pragma:no-cacheSec-WebSocket-Extensions:permessage-deflate;client_max_window_bitsSec-WebSocket-Key:PJS0iPLxrL0ueNPoAFUSiA==Sec-WebSocket-Version:13Upgrade:websocketUser-Agent:Mozilla/5.0(iPhone;CPUiPhoneOS11_0likeMacOSX)AppleWebKit/604.1.38(KHTML,likeGecko)Version/11.0Mobile/15A372Safari/604.1请求包说明:必须是有效的http请求格式;HTTP请求方式必须是GET,协议不能小于1.1如:Get/HTTP/1.1;必须包含Upgrade头字段,其值为“websocket”,用于告诉服务器此连接需要升级为websocket;必须包含“Connection”头字段,其值为“Upgrade”;必须包含“Sec-WebSocket-Key”头字段,它的值是一个随机的16字节长度,以base64字符序列编码;如果请求来自浏览器客户端,则还必须包含Origin标头字段此标头字段用于防止未经授权的跨域脚本攻击。服务器可以决定是否接受来自Origin的WebSocket连接;必须包含“Sec-webSocket-Version”头域,为当前协议的版本号,当前值必须为13;可以包含“Sec-WebSocket-Protocol”,表示当前协议支持的协议列表客户端(应用程序),服务器选择一个或以不可接受的协议响应;可能包括“Sec-WebSocket-Extensions”,协议扩展,某类协议可能支持多种扩展,通过扩展可以实现协议增强;可能包括任何其他字段,例如cookie。ResponsepacketResponsepacketdescription:Connection:UpgradeSec-WebSocket-Accept:I4jyFwm0r1J8lrnD3yN+EvxTABQ=Sec-WebSocket-Extensions:permessage-deflateUpgrade:websocket必须包含Upgrade头字段,其值为“websocket”;必须包含Connection头域,其值为“Upgrade”;必须包含Sec-WebSocket-Accept头域,其值是发送请求包“Sec-”WebSocket-Key的值与字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”拼接而成,并且然后对拼接后的字符串进行sha-1运算,再进行base64编码,即“Sec-WebSocket-Accept”;响应包中冒号后有一个空格;末尾需要两个空行作为响应包的结尾请求数据EIO:3transport:websocketsid:8Uehk2UumXoHVJRzAAAAEIO:3表示使用engine.io协议版本3transport表示传输类型sid:sessionid(String)FramesWebSocket协议使用frames(Frame)发送和接收数据,在控制台->Frames可以查看发送的帧数据。帧数据前的数字是什么意思?这是Engine.io协议,其中数字是数据包编码:[]0open-打开新传输时从服务器发送(重新检查)1close-请求关闭此传输,但不会关闭连接本身。2ping-由客户端发送。服务器应使用包含相同数据的pong数据包回复客户端发送:2probeframe3pong-由服务器发送以响应ping数据包。服务器发送:3probe,响应客户端4消息-实际消息,客户端和服务器应使用数据调用它们的回调。5升级-在engine.io切换传输之前,它会测试服务器和客户端是否可以通过此传输进行通信。如果此测试成功,客户端将发送一个升级数据包,请求服务器刷新其在旧传输上的缓存并切换到新传输。6noop-noop数据包。主要用于在接收到传入的WebSocket连接时强制执行轮询周期。例子上面的截图是上面例子中数据传输的例子。下面分析一下大概的流程:握手成功后客户端会发送2个探测检测帧,服务器会发送一个响应帧。noopframedetectionframeinspection6pass后,client停止轮询请求,将传输通道转移到websocket连接,转移到websocket后,再开始定时(默认25秒)ping/pongclient和service终端发送和接收数据,4表示engine.io的message消息,后面是发送和接收的消息内容。为了知道Client和Server之间的连接是否正常,项目中使用的ClientSocket和ServerSocket都有一个心跳线程。该线程主要是检测客户端和服务端是否正常连接,客户端和服务端是否正常连接主要靠pingpong进程来保证。心跳以25m的间隔周期性发送,这是socket.io默认设置的,上图也可以观察到。这个间隔可以通过配置修改。参考engine.io-protocol参考文章Web实时推送技术总结Engine.io原理详解广而告之欢迎讨论,点个赞再走?????~