zoukankan      html  css  js  c++  java
  • 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/

  • 相关阅读:
    Median Value
    237. Delete Node in a Linked List
    206. Reverse Linked List
    160. Intersection of Two Linked Lists
    83. Remove Duplicates from Sorted List
    21. Merge Two Sorted Lists
    477. Total Hamming Distance
    421. Maximum XOR of Two Numbers in an Array
    397. Integer Replacement
    318. Maximum Product of Word Lengths
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7221177.html
Copyright © 2011-2022 走看看