标签: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工作文件夹/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>
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 ?至此所有的编码开发工作所有完毕了。在浏览器中打开http://demo.plhwin.com/chat/就能够看到效果了。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(); } }; })();
上面全部的client和服务端的代码能够从Github上获得,地址:https://github.com/plhwin/nodejs-socketio-chat
git clone https://github.com/plhwin/nodejs-socketio-chat.git
也能够放在Node.js的WebServer中。后面的server目录里的代码是websocket服务端代码。放在Node.js环境中
,使用npm安装完 express 和 socket.io 后,node index.js 启动后端服务就能够了。
本例仅仅是一个简单的Demo,留下2个有关项目扩展的思考:
1、如果是一个在线客服系统,里面有很多的公司使用你的服务,每一个公司自己的用户能够通过一个专属URL地址
进入该公司的聊天室,聊天是一对一的,每一个公司能够新建多个客服人员,每一个客服人员能够同一时候和client的多个用户聊天。
2、又如果是一个在线WebIM系统,实现类似微信。qq的功能,client能够看到好友在线状态,在线列表。加入好友,
删除好友。新建群组等。消息的发送除了支持主要的文字外。还能支持表情、图片和文件。
标签:named 社区 name this minimum 更新 inpu 随机 names
原文地址:http://www.cnblogs.com/blfbuaa/p/7221177.html