赞
踩
开篇
最近几年随着各大浏览器以及Web标准技术的蓬勃发展, 基于浏览器的B/S应用已经渐渐变得与我们过去所认知的完全不同。
尤其是移动互联设备的兴起以及HTML5的诞生,将Web开发带入了一个全新的时代。
借助各种先进的技术,Web应用有了更好的用户体验 更强大的功能和性能,很多原本只能在桌面原生应用中做到的事情现在也可以在Web中完成,甚至可以完成的更好。同时,这些新兴的Web技术也逐渐的模糊了C/S和B/S的界线,很好的结合了两者的优点,摒弃其缺点。
也许在未来 一个Web 便可以承载起软件开发者共同的梦想。
(为什么Web2.0的热浪席卷全球? 为什么google要力推ChromeOS,为什么惠普要大力发展WebOS,为什么SaaS大行其道… )
如果说 Web是计算机应用的未来, 那么以HTML5和JS为代表的标准技术则是Web的未来.
在接下来的若干篇文章中(也可能仅此一篇…囧),我打算向大家介绍一些新兴的Web技术,其中包括HTML5/CSS3/"全新的"JavaScript(为什么叫“全新的”???)/无线互联技术。
当然 由于时间和能力有限 不能向大家做全面透测的讲解, 而且我也没必要做这种细致的 教学性质的讲解,因为网络上优秀的相关的技术资料已经多得不能更多了。
我希望这些文章能够帮助对Web开发不感兴趣或者不甚了解的朋友开阔一下的视野,同时能够从一个相对独特的有趣的视角来向大家展示这些技术。
并且希望在文章中能够传达出我个人的一些思考。
好了 废话先说道这里,下面开始进入正题吧。
WebSocket简介
这次想向大家介绍的是 HTML5中引入的一个非常诱人的新特性 : WebSocket 。
在很久很久以前, 一个叫做Ajax的东西曾经给Web开发带来了一场革命。 Ajax的核心技术就是一个叫做 XMLHttpRequest的异步传输组件。
利用该组件 我们可以在不刷新页面 不提交Form的前提下,利用JS与服务端进行数据的交互,交互过程对于客户端来说是异步无阻塞的。大致过程是:客户端利用XMLHttpRequest向服务端发送一个异步请求(客户端向服务端传递数据),等待服务端响应,服务端接受到请求后做一些处理然后返回响应(服务端向客户端传递数据),客户端接收到数据后与服务器断开连接,交互结束。这种交互依然是基于标准的HTTP协议,是一种无状态的 响应式的短连接。
而WebSocket的出现,则可以看作是XMLHttpRequest的升级加强版,它除了也具备“在不刷新页面 不提交Form的前提下,利用JS与服务端进行数据的交互”的特性外,还具备了“有状态,高速,双向互通,持续长连接”等特点。
WebSocket是一种全双工的双向通信技术,主要目的是在web浏览器和web服务器之间提供一种类似TCP scoket的双向的 持续性的 有状态的通讯方式.
它的出现,使得服务端推送,B/S之间的长连接成为了可能。有了WebSocket,在Web页面中利用JS+HTML5等标准技术(而无需Flash ActiveX SilverLight等非标准技术)来实现复杂的 高实时性 高交互性的网络应用不再是天方夜谭(例如游戏股票行情 实时监控数据 远程桌面等等)。
Websocket 由两部分组成,一部分是浏览器中的WebSocket API(隶属于HTML5规范), 由W3C组织制订;一部分是WebSocket协议(可以用任何语言来实现), 由IETF组织制订。
虽然WebSocket 协议看起来更接近TCP scoket协议,但它的定位却是HTTP 1.1的一个升级,因此具备了HTTP协议的很多优点。例如强大的穿墙能力,兼容HTTP反向代理等等。
客户端通过WebSocket与服务端进行通讯时,只有第一握手时交互的信息比较复杂,在握手成功之后的便进入双向长连接的数据传输阶段,此时传输的几乎只是存数据,性能很高。WebSocket的数据传输是基于帧的方式: 0x00 表示数据开始, 0xff表示数据结束,数据以utf-8编码.
(目前 浏览器与服务端之只能传递文本,不能传递二进制。WebSocket协议本身是支持二进制的,只是浏览器中的脚本语言暂时不具备二进制数据处理能力。)
目前支持WebSocket的浏览器有 FireFox 4.0+ ,chrome 4.0+ ,safari 5.0+ 。
而WebSocket Server的实现则多种多样,几乎每一种流行的服务端编程语言都能找到很多的实现。
Java中比较出色的实现是 JBoss netty , jetty 7+
WebSocket 客户端与服务端通讯的API非常简单, 在浏览器中 使用js来调用:
{code}
// 链接到 WebSocket Server 192.168.0.52:8000
var ws = new WebSocket('ws://192.168.0.52:8000/');
// 向服务端发送信息 : Hello World
ws.send('Hello World')
// 一个事件,当接收到服务端发来的消息时触发.
ws.onmessage = function(event) {
//event.data的值就是服务端发送来的数据.
var data=event.data;
}
// (注意 以上操作全部是异步的).
{code}
一个示例
关于WebSocket的更多细节大家可以去网上找到很多, 在这里我不再详述。 下面我主要通过一个示例,来展示一下 WebSocket为web应用所带来的全新体验。
网上关于WebSocket的示例绝大多数都是聊天室。过去在web中实现聊天室使用的往往是轮询的方式,即:每隔一段时间去从服务端拉一些数据过来。虽然有comet bigpipe等技术,但是其本质仍然是“拉” 而非服务端“推送”。 有了WebSocket,服务端推送就很好实现了。
既然网上聊天室的例子已经很多了, 我自然不会再拿聊天室做例子。下面我来说一说这个示例所要实现的效果。
我们先来设想一个网络游戏中的普通场景: 你登录游戏后,控制你的人物来到游戏世界中的某个广场。你可以在电脑屏幕上看到哪些玩家和你处在同一个广场,并且可以看到其他玩家的行动,而其他玩家也能看到你的行动。
把这个场景做一个大大的简化,就是这个示例所要演示的内容了。
示例演示内容:
用支持WebSocket的浏览器(建议Chrome 6 或 safari 5)访问服务器上的 gtest.html页面, 输入自己的昵称,点击“加入”按钮。
之后可以在浏览器窗口中看到一个写有自己名字的蓝色方块(这就是你控制的人物),此时通过 W A S D键 可以控制这个方块四处移动(需关闭输入法)。
而此时 如果别人也来访问这个页面并且也“加入”,则你可以在自己的浏览器上看到“其他人”(其他蓝色方块),而且如果此时其他人移动他们自己的方块,你也能够实时的看到,当然其他人也能看到你。
这里面与WebSocket相关的关键技术就是同步所有用户方块的坐标。任何一个用户的方块坐标发生变化时,都通知服务端,服务端拿到数据后,广播给所有通过WebSocket连接的浏览器, 浏览器拿到广播的数据后 更新页面显示的内容。
当然该示例存在很多问题(例如服务端也应该维护一份各个用户的状态信息,并且在必要的时候同步给各个客户端),并不是一个完备的有实际意义的WebSocket应用,不过从中依然可以对WebSocket的强大有一定程度的了解。
运行效果见下图:
客户端js代码 (部分细节见注释):
{code}
//WebSocket对象
var ws = null;
// 所有用户集合
var players=null;
//当前用户(自己)
var player=null;
//加入
function start(){
//初始化
players={};
player={
id:null,
name: document.getElementById('player_name').value,
x: getRandom(50,500),
y: getRandom(50,300),
speed:5,
color:'#ddeeff'
}
connWS();
}
//离开
function end(){
window.location.reload();
}
//初始化并连接到WebSocket服务器
function connWS(){
//连接
ws = new WebSocket('ws://192.168.0.52:8000/');
//当接收到服务端的数据时
// 服务端传回的数据有三种格式:
// Connection: id 该id的用户加入 (只发送给自己,目的是拿到服务端生成的id)
// Disconnected: id 该id的用户离开
// <id> {x : 123, y:300, name : "Tom"} 该id的用户的状态(坐标和昵称)
ws.onmessage = function(e) {
var data=e.data;
var isNew=false;
if (data.indexOf('Connection: ')==0){
isNew=true;
var id=data.substring('Connection: '.length);
player.id=id;
ws.send(JSON.stringify(player));
run();
}else if(data.indexOf('Disconnected: ')==0){
var id=data.substring('Disconnected: '.length);
delete players[id];
removePlayer(id);
}else if (data.indexOf('<')==0){
var idx=data.indexOf('>');
var id=data.substring(1,idx);
var msg=JSON.parse(data.substring(idx+1));
players[msg.id]=msg;
}
};
}
// 更新指定用户状态
function updatePlayer(id,msg){
var div=document.getElementById('p_'+id);
if (!div){
div=document.createElement('div');
div.className='player';
div.id='p_'+id;
document.body.appendChild(div);
}
div.style.backgroundColor=msg.color;
div.innerHTML=msg.name;
div.style.left=msg.x;
div.style.top=msg.y;
}
//移除指定用户
function removePlayer(id){
delete players[id];
var div =document.getElementById('p_'+id);
if (div){
document.body.removeChild(div);
}
}
/
/
// 主循环, 每30毫秒更新一下自己的坐标并更新页面显示内容。
function run(){
update();
draw();
setTimeout( run , 30);
}
// 更新自己的坐标
function update(){
// W: 87, A: 65, S: 83, D: 68,
var W=window.KEY[87],
A=window.KEY[65],
S=window.KEY[83],
D=window.KEY[68]
if (W && S || !W && !S){
}else if (W){
player.y-= player.speed;
}else if (S){
player.y+=player.speed;
}
if (A && D || !A && !D){
}else if (A){
player.x-=player.speed;
}else if (D){
player.x+=player.speed;
}
//把自己的坐标发送给服务器
ws.send(JSON.stringify(player));
}
//更新页面显示内容
function draw(){
for (var id in players){
//console.log(id)
updatePlayer(id,players[id])
}
}
//刷新页面之前 断开与WebSocket服务器的连接
window.addEventListener('unload',function(e){
ws.close();
}, true);
//取得指定区间内的随机正整数数
function getRandom(lower,higher){
lower=lower||0;
higher=higher||9999;
return Math.floor((higher - lower + 1) * Math.random()) + lower;
}
//页面初始化
function init(){
// 键盘事件管理
window.KEY={};
window.addEventListener('keydown',function(e){
window.KEY[e.keyCode] = true;
}, true);
window.addEventListener('keyup',function(e){
window.KEY[e.keyCode] = false;
}, true);
var id= getRandom(10,99999);
document.getElementById('player_name').value="test"+id;
}
{code}
服务端js代码 (服务端也使用js所写) :
{code}
var sys = require("sys"),
ws = require("./ws");
//创建WebSocket Server
var server = ws.createServer();
//当有客户端接入时
server.addListener("connection", function(conn){
//向接入的客户端发送信息
conn.send("Connection: "+conn.id);
//当收到客户端发送的信息时
conn.addListener("message", function(message){
//向所有客户端发送信息.
server.broadcast("<"+conn.id+"> "+message);
});
});
//当有客户端断开连接时
server.addListener("close", function(conn){
//向所有客户端发送信息.
server.broadcast("Disconnected: "+conn.id);
});
//启动服务 监听8000端口
server.listen(8000);
{code}
服务端基于一个国外网友写的websocket server组件所写.贴出来的部分代码并不能直接运行,需要相关的环境支持.
大家只要看一下思路就好 其实挺简单的.基于java语言也可以很方便的实现,
可能会有朋友疑惑,JS怎么能写socket server? 其实如今JS已经进入了一个全新的阶段(这就是文章前面我为什么要说“全新的”JS)。
要想对这个所谓的“全新的”JS有更进一步的了解,敬请期待后续文章(如果还有的话)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。