码迷,mamicode.com
首页 > Web开发 > 详细

HTML5新特性之WebSocket

时间:2017-07-22 15:44:54      阅读:250      评论:0      收藏:0      [点我收藏+]

标签:named   社区   name   this   minimum   更新   inpu   随机   names   

一、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="width:260px;margin:200px auto;">
                请先输入你在聊天室的昵称
                <br/>
                <br/>
                <input type="text" style="width:180px;" placeholder="请输入用户名" id="username" name="username" />
				<input type="button" style="width:50px;" value="提交" onclick="CHAT.usernameSubmit();"/>
            </div>
        </div>
        <div id="chatbox" style="display:none;">
            <div style="background:#3d3d3d;height: 28px; width: 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/

HTML5新特性之WebSocket

标签:named   社区   name   this   minimum   更新   inpu   随机   names   

原文地址:http://www.cnblogs.com/blfbuaa/p/7221177.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!