• HTML5新特性之WebSocket


    一、WebSocket简单介绍:

         谈到Web实时推送,就不得不说WebSocket。在WebSocket出现之前,非常多站点为了实现实时推送技术。通常採用的方案

    是轮询(Polling)和Comet技术,Comet又可细分为两种实现方式,一种是长轮询机制。一种称为流技术。这两种方式实际上是对

    轮询技术的改进。这些方案带来非常明显的缺点,须要由浏览器对server发出HTTP request。大量消耗server带宽和资源。面对

    这样的状况。HTML5定义了WebSocket协议,能更好的节省server资源和带宽并实现真正意义上的实时推送。

         WebSocket协议本质上是一个基于TCP的协议。它由通信协议和编程API组成,WebSocket可以在浏览器和server之间建立

    双向连接,以基于事件的方式,赋予浏览器实时通信能力。既然是双向通信。就意味着server端和client能够同一时候发送并响应请

    求。而不再像HTTP的请求和响应。

         为了建立一个WebSocket连接。client浏览器首先要向server发起一个HTTP请求,这个请求和通常的HTTP请求不同。包括

    了一些附加头信息。当中附加头信息”Upgrade: WebSocket”表明这是一个申请协议升级的HTTP请求,server端解析这些附加的

    头信息然后产生应答信息返回给client。client和server端的WebSocket连接就建立起来了。两方就能够通过这个连接通道自由

    的传递信息。而且这个连接会持续存在直到client或者server端的某一方主动的关闭连接。

         一个典型WebSocketclient请求头:


         注意:WebSocket是HTML5中新增的一种通信协议,这意味着一部分老版本号浏览器(主要是IE10下面版本号)并不具备这个功能。 

    通过百度统计的公开数据显示,IE8眼下仍以33%的市场份额占领榜首,好在chrome浏览器市场份额逐年上升,如今以超过26%的

    市场份额位居第二,同一时候微软前不久宣布停止对IE6的技术支持并提示用户更新到新版本号浏览器。这个以前让无数前端project师为之头

    疼的浏览器有望退出历史舞台,再加上差点儿全部的智能手机浏览器都支持HTML5,所以使得WebSocket的实战意义大增。可是不管

    怎样,我们实际的项目中,仍然要考虑低版本号浏览器的兼容方案:在支持WebSocket的浏览器中採用新技术,而在不支持WebSocke

    t的浏览器里启用Comet来接收发送消息。

         浏览器支持列表:



    二、WebSocket实战:

         本文将以多人在线聊天应用作为实例场景。我们先来确定这个聊天应用的基本需求。

    需求分析:

    1、兼容不支持WebSocket的低版本号浏览器。

    2、同意client有同样的username。

    3、进入聊天室后能够看到当前在线的用户和在线人数。

    4、用户上线或退出,全部在线的client应该实时更新。

    5、用户发送消息,全部client实时收取。


         在实际的开发过程中。为了使用WebSocket接口构建Web应用。我们首先须要构建一个实现了 WebSocket规范的服务端。

    服务端的实现不受平台和开发语言的限制,仅仅须要遵从WebSocket规范就可以。眼下已经出现了一些比較成熟的 WebSocket

    服务端实现,比方本文使用的Node.js+Socket.IO。

    为什么选用这个方法呢?以下将先进行介绍。


    Node.js:

         Node.js採用C++语言编写而成。它不是Javascript应用,而是一个Javascript的执行环境。据Node.js创始人 Ryan Dahl回顾。

    他最初希望採用Ruby来写Node.js,可是后来发现Ruby虚拟机的性能不能满足他的要求,后来他尝试採用V8引擎。所以选择

    了 C++语言。

         Node.js支持的系统包含*nux、Windows,这意味着程序猿能够编写系统级或者server端的Javascript代码。交给 Node.js来

    解释运行。Node.js的Web开发框架Express。能够帮助程序猿高速建立web网站,从2009年诞生至今,Node.js的 成长的速度

    有目共睹,其发展前景获得了技术社区的充分肯定。


    Socket.IO:
         Socket.IO是一个开源的WebSocket库,它通过Node.js实现WebSocket服务端,同一时候也提供clientJS库。

    Socket.IO支持以

    事件为基础的实时双向通讯。它能够工作在不论什么平台、浏览器或移动设备。

         Socket.IO支持4种协议:WebSocket、htmlfile、xhr-polling、jsonp-polling。它会自己主动依据浏览 器选择适合的通讯方式,

    从而让开发人员能够聚焦到功能的实现而不是平台的兼容性。同一时候Socket.IO具有不错的稳定性和性能。


    终于效果:


    开发步骤

    (1)、安装Node.js

         依据操作系统,去Node.js官网下载安装。假设安装成功。在命令行输入node -v和npm -v应该能看到对应的版本。

    如图:


    (2)、搭建WebSocket服务端

         这个环节我们尽可能的考虑真实生产环境。把WebSocket后端服务搭建成一个线上能够用域名訪问的服务,假设你是在本地开发环境,

    能够换成本地ip地址,或者使用一个虚拟域名指向本地ip。

         先进入到你的工作文件夹。比方 /workspace/wwwroot/plhwin/realtime.plhwin.com,新建一个名为 package.json的文件,内容例如以下:

    {
      "name": "realtime-server",
      "version": "0.0.1",
      "description": "my first realtime server",
      "dependencies": {}
    }
         接下来使用npm命令安装express和socket.io
    npm install --save express
    npm install --save socket.io
         成功安装后。应该能够看到工作文件夹下生成了一个名为node_modules的文件夹,里面各自是express和socket.io,接下来能够開始编写

    服务端的代码了。新建一个文件:index.js

    var app = require('express')();
    var http = require('http').Server(app);
    var io = require('socket.io')(http);
    app.get('/', function(req, res){
    	res.send('<h1>Welcome Realtime Server</h1>');
    });
    http.listen(3000, function(){
    	console.log('listening on *:3000');
    });
          命令行执行node index.js。假设一切顺利,你应该会看到返回的listening on *:3000字样,这说明服务已经成功搭建了。

    此时浏览器中打开

    http://localhost:3000应该能够看到正常的欢迎页面。

    假设你想要让服务执行在线上server。而且能够通过域名訪问的话,能够使用Nginx做代理。在nginx.conf中加入例如以下配置,然后将域名

    (比方:realtime.plhwin.com)解析到serverIP就可以。

     server
      {
        listen       80;
        server_name  realtime.plhwin.com;
        location / {
          proxy_pass http://127.0.0.1:3000;
        }
      }
         完毕以上步骤,http://realtime.plhwin.com:3000的后端服务就正常搭建了。

    如图:

    (3)、服务端代码实现

         前面讲到的index.js执行在服务端,之前的代码仅仅是一个简单的WebServer欢迎内容,让我们把WebSocket服务端完整的实现代码增加进去,

    整个服务端就能够处理client的请求了。完整的index.js代码例如以下:

    var app = require('express')();
    var http = require('http').Server(app);
    var io = require('socket.io')(http);
    
    app.get('/', function(req, res){
    	res.send('<h1>Welcome Realtime Server</h1>');
    });
    
    //在线用户
    var onlineUsers = {};
    //当前在线人数
    var onlineCount = 0;
    
    io.on('connection', function(socket){
    	console.log('a user connected');
    	
    	//监听新用户增加
    	socket.on('login', function(obj){
    		//将新增加用户的唯一标识当作socket的名称。后面退出的时候会用到
    		socket.name = obj.userid;
    		
    		//检查在线列表,假设不在里面就增加
    		if(!onlineUsers.hasOwnProperty(obj.userid)) {
    			onlineUsers[obj.userid] = obj.username;
    			//在线人数+1
    			onlineCount++;
    		}
    		
    		//向全部client广播用户增加
    		io.emit('login', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
    		console.log(obj.username+'增加了聊天室');
    	});
    	
    	//监听用户退出
    	socket.on('disconnect', function(){
    		//将退出的用户从在线列表中删除
    		if(onlineUsers.hasOwnProperty(socket.name)) {
    			//退出用户的信息
    			var obj = {userid:socket.name, username:onlineUsers[socket.name]};
    			
    			//删除
    			delete onlineUsers[socket.name];
    			//在线人数-1
    			onlineCount--;
    			
    			//向全部client广播用户退出
    			io.emit('logout', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
    			console.log(obj.username+'退出了聊天室');
    		}
    	});
    	
    	//监听用户公布聊天内容
    	socket.on('message', function(obj){
    		//向全部client广播公布的消息
    		io.emit('message', obj);
    		console.log(obj.username+'说:'+obj.content);
    	});
      
    });
    
    http.listen(3000, function(){
    	console.log('listening on *:3000');
    });

    四、client代码实现

         进入client工作文件夹/workspace/wwwroot/plhwin/demo.plhwin.com/chat。新建一个index.html:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <meta name="format-detection" content="telephone=no"/>
            <meta name="format-detection" content="email=no"/>
            <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport">
            <title>多人聊天室</title>
            <link rel="stylesheet" type="text/css" href="./style.css" />
            <!--[if lt IE 8]><script src="./json3.min.js"></script><![endif]-->
            <script src="http://realtime.plhwin.com:3000/socket.io/socket.io.js"></script>
        </head>
        <body>
            <div id="loginbox">
                <div style="260px;margin:200px auto;">
                    请先输入你在聊天室的昵称
                    <br/>
                    <br/>
                    <input type="text" style="180px;" placeholder="请输入用户名" id="username" name="username" />
    				<input type="button" style="50px;" value="提交" onclick="CHAT.usernameSubmit();"/>
                </div>
            </div>
            <div id="chatbox" style="display:none;">
                <div style="background:#3d3d3d;height: 28px;  100%;font-size:12px;">
                    <div style="line-height: 28px;color:#fff;">
                        <span style="text-align:left;margin-left:10px;">Websocket多人聊天室</span>
                        <span style="float:right; margin-right:10px;"><span id="showusername"></span> | 
    					<a href="javascript:;" onclick="CHAT.logout()" style="color:#fff;">退出</a></span>
                    </div>
                </div>
                <div id="doc">
                    <div id="chat">
                        <div id="message" class="message">
    <div id="onlinecount" style="background:#EFEFF4; font-size:12px; margin-top:10px; margin-left:10px; color:#666;">
    </div>
                        </div>
                        <div class="input-box">
                            <div class="input">
    <input type="text" maxlength="140" placeholder="请输入聊天内容,按Ctrl提交" id="content" name="content">
                            </div>
                            <div class="action">
                                <button type="button" id="mjr_send" onclick="CHAT.submit();">提交</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <script type="text/javascript" src="./client.js"></script>
        </body>
    </html>

    上面的html内容本身没有什么好说的,我们主要看看里面的4个文件请求:

    1、realtime.plhwin.com:3000/socket.io/socket.io.js

    2、style.css

    3、json3.min.js

    4、client.js

    第1个JS是Socket.IO提供的clientJS文件,在前面安装服务端的步骤中,当npm安装完socket.io并搭建起WebServer后,这个JS文件就能够正常訪问了。

    第2个style.css文件没什么好说的。就是样式文件而已。

    第3个JS仅仅在IE8下面版本号的IE浏览器中载入,目的是让这些低版本号的IE浏览器也能处理json。这是一个开源的JS,详见:http://bestiejs.github.io/json3/

    第4个client.js是完整的客户端的业务逻辑实现代码,它的内容例如以下:

    (function () {
    	var d = document,
    	w = window,
    	p = parseInt,
    	dd = d.documentElement,
    	db = d.body,
    	dc = d.compatMode == 'CSS1Compat',
    	dx = dc ?

    dd: db, ec = encodeURIComponent; w.CHAT = { msgObj:d.getElementById("message"), screenheight:w.innerHeight ? w.innerHeight : dx.clientHeight, username:null, userid:null, socket:null, //让浏览器滚动栏保持在最低部 scrollToBottom:function(){ w.scrollTo(0, this.msgObj.clientHeight); }, //退出,本例仅仅是一个简单的刷新 logout:function(){ //this.socket.disconnect(); location.reload(); }, //提交聊天消息内容 submit:function(){ var content = d.getElementById("content").value; if(content != ''){ var obj = { userid: this.userid, username: this.username, content: content }; this.socket.emit('message', obj); d.getElementById("content").value = ''; } return false; }, genUid:function(){ return new Date().getTime()+""+Math.floor(Math.random()*899+100); }, //更新系统消息,本例中在用户增加、退出的时候调用 updateSysMsg:function(o, action){ //当前在线用户列表 var onlineUsers = o.onlineUsers; //当前在线人数 var onlineCount = o.onlineCount; //新增加用户的信息 var user = o.user; //更新在线人数 var userhtml = ''; var separator = ''; for(key in onlineUsers) { if(onlineUsers.hasOwnProperty(key)){ userhtml += separator+onlineUsers[key]; separator = '、'; } } d.getElementById("onlinecount").innerHTML = '当前共同拥有 '+onlineCount+' 人在线,在线列表:'+userhtml; //增加系统消息 var html = ''; html += '<div class="msg-system">'; html += user.username; html += (action == 'login') ? ' 增加了聊天室' : ' 退出了聊天室'; html += '</div>'; var section = d.createElement('section'); section.className = 'system J-mjrlinkWrap J-cutMsg'; section.innerHTML = html; this.msgObj.appendChild(section); this.scrollToBottom(); }, //第一个界面用户提交用户名 usernameSubmit:function(){ var username = d.getElementById("username").value; if(username != ""){ d.getElementById("username").value = ''; d.getElementById("loginbox").style.display = 'none'; d.getElementById("chatbox").style.display = 'block'; this.init(username); } return false; }, init:function(username){ /* 客户端依据时间和随机数生成uid,这样使得聊天室用户名称能够反复。 实际项目中,假设是须要用户登录,那么直接採用用户的uid来做标识就能够 */ this.userid = this.genUid(); this.username = username; d.getElementById("showusername").innerHTML = this.username; this.msgObj.style.minHeight = (this.screenheight - db.clientHeight + this.msgObj.clientHeight) + "px"; this.scrollToBottom(); //连接websocket后端server this.socket = io.connect('ws://realtime.plhwin.com:3000'); //告诉server端实用户登录 this.socket.emit('login', {userid:this.userid, username:this.username}); //监听新用户登录 this.socket.on('login', function(o){ CHAT.updateSysMsg(o, 'login'); }); //监听用户退出 this.socket.on('logout', function(o){ CHAT.updateSysMsg(o, 'logout'); }); //监听消息发送 this.socket.on('message', function(obj){ var isme = (obj.userid == CHAT.userid) ? true : false; var contentDiv = '<div>'+obj.content+'</div>'; var usernameDiv = '<span>'+obj.username+'</span>'; var section = d.createElement('section'); if(isme){ section.className = 'user'; section.innerHTML = contentDiv + usernameDiv; } else { section.className = 'service'; section.innerHTML = usernameDiv + contentDiv; } CHAT.msgObj.appendChild(section); CHAT.scrollToBottom(); }); } }; //通过“回车”提交用户名 d.getElementById("username").onkeydown = function(e) { e = e || event; if (e.keyCode === 13) { CHAT.usernameSubmit(); } }; //通过“回车”提交信息 d.getElementById("content").onkeydown = function(e) { e = e || event; if (e.keyCode === 13) { CHAT.submit(); } }; })();

    至此所有的编码开发工作所有完毕了。在浏览器中打开http://demo.plhwin.com/chat/就能够看到效果了。

    上面全部的client和服务端的代码能够从Github上获得,地址:https://github.com/plhwin/nodejs-socketio-chat

    git clone https://github.com/plhwin/nodejs-socketio-chat.git 

        下载本地后有两个目录 client 和 server,client目录是客户端源代码,能够放在Nginx/Apache的WebServer中,

    也能够放在Node.js的WebServer中。后面的server目录里的代码是websocket服务端代码。放在Node.js环境中

    ,使用npm安装完 express 和 socket.io 后,node index.js 启动后端服务就能够了。

         本例仅仅是一个简单的Demo,留下2个有关项目扩展的思考:

         1、如果是一个在线客服系统,里面有很多的公司使用你的服务,每一个公司自己的用户能够通过一个专属URL地址

    进入该公司的聊天室,聊天是一对一的,每一个公司能够新建多个客服人员,每一个客服人员能够同一时候和client的多个用户聊天。

         2、又如果是一个在线WebIM系统,实现类似微信。qq的功能,client能够看到好友在线状态,在线列表。加入好友,

    删除好友。新建群组等。消息的发送除了支持主要的文字外。还能支持表情、图片和文件。

    来自:http://www.plhwin.com/2014/05/28/nodejs-socketio/

  • 相关阅读:
    如何从人群中看出真正优秀的人
    JAVA的面向对象编程--------课堂笔记
    uC/OS-II源码分析
    肖申克的救赎
    汇编中基本的数据类型
    十进制负数转换为二进制、八进制、十六进制的知识分享
    x86汇编指令详解
    Angularjs
    事件处理类型的三种方法
    正则表达式的使用
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7221177.html
Copyright © 2020-2023  润新知