WebSocket分享
WebSocket_Share_Mol
@(我的分享)[websocket]
按照惯例,扯闲篇
大家都是好几年经验的老程序员了,但是我觉得几乎没有人能把http协议说明白,当然也包括我。
不过,正好有一个契机让我来描述一下,我的理解。这个契机就是,你们都是有驾照的人了。不管有没有真正地上过路,科目一的知识还是记得的吧。忘了?没关系。我要说的其实只有车道的概念。
遥想http
我们都知道,http是一种协议,就像tcp/ip一样。而且它是一种“无状态”的协议。它还有一个特点,就是每次http请求都对应并且唯一对应一个响应。它看起来像是这样:

我们把浏览器和服务端之间的“通路”看成一条洗车走的公路,我们就会发现,这条路是“双向单车道”,尽管科目一里面没有这种路。其实你只要想一下你家门口的那条很窄的土路就可以了。在某一个特定的时刻,这条路上只能有一个方向是可以通行的。比如嚣张哥11点从东向西走,11点10分从西向东走。这样是没有问题的。肯定不能说嚣张哥从东向西走的时候,还有另外一辆车从西向东走。
常见的场景
如果上面的描述还是足够抽象,以致于你看不懂,也没有关系。我们来想一下,你在写webForm程序的时候,是否是这样写的,在一个订单的aspx页面上放一个按钮,你在这个按钮的点击事件里面,向服务器发起请求,获取订单的实时状态。这就是http的原始形态。它表现为:客户端主动发起请求,服务端被动回应请求。只要你有足够的耐心,不停地点击按钮,你就可以看到订单状态的变化。
对于你每次点击按钮的操作来说,就是你和服务器之间建立了一个通道,这个通道存在的时间非常短暂。在这个通道上面,浏览器先向服务端发起查询请求(图中的Request),服务器再回应给你状态响应(图中的Response),你就可以实时地看到订单状态了。
弊端
这样做的话,有明显的弊端。首先,用户体验是非常糟糕的,因为用户必须不停地点击按钮,才能拿到最新的订单状态;其次,客户端频繁地发送请求,也会导致服务器不堪重负,长此以往,不宕机都难。
临时解决方案
我之所以叫它是临时解决方案,是因为下面所述的方案并没有从根本上解决问题,而只是“看起来很好”
方案 1
大家的ajax用得一定非常熟了,至少比我熟,至少李老板比我熟。
我可以在页面上写一段ajax程序,向服务器发送请求,请求的内容就是获取订单状态。这个请求可以隔5秒钟获取一次。这样的话,用户就不用频繁地点击按钮来获取状态了。而且也不会造成页面刷新。用户体验一下子就好了许多。
服务端代码看起来像是这样:
public Action GetOrderStatus(int orderid)
{
var orderModel=bll.GetModel(orderid);//根据id获取对应的订单实体
var orderStatusOld=orderModel.Status;//获取当前的订单状态
return Content(orderModel.Status);//将最新的状态返回给客户端
}
客户端伪代码看起来像是这样:
$(function(){
setInterval(function(){
$.ajax(
url:你的服务器地址,
onSuccess:function(data){
$('#界面标签').html(data.Status);//将当前状态显示到界面上
}
);
},'5000')//当前函数每5秒执行一次
});
这种方案改良了用户体验,但还是没有解决服务负荷过大的问题。因为我们总是在不停地向服务器发送请求。
方案2
我现在要把上面的方案进行改良,让服务器不要有那么大的压力。我的目的是:客户端只发一次请求,就可以得到订单状态的变化。
我还打算用ajax来实现。但是我的服务端就需要做一些改动了。我让服务端轮循订单状态,当有订单变化的时候,把状态返回给客户端。服务端代码看起来像是这样:
public Action GetOrderStatus(int orderid)
{
var orderModel=bll.GetModel(orderid);//根据id获取对应的订单实体
var orderStatusOld=orderModel.Status;//获取当前的订单状态
while(true)
{
orderModel=bll.GetModel(orderid);//在循环中重新获取订单实体
var orderStatusNew=orderModel=orderModel.Status;//获取订单最新的状态
if(orderStatusNew!=orderStatusOld)
{
break;//当订单状态发生变化的时候,中止循环
}
}
return Content(orderModel.Status);//将最新的状态返回给客户端
}
客户端的js代码看起来像是这样:
$(function(){
$.ajax(
url:服务器地址/GetOrderStatus/Id
onSuccess:function(data){
$('#页面标签').html(data.Status);
}
);
});
这样做的好处就是,浏览器只需要发送一次请求,就可以得到最新的订单状态。它的弊端就是,服务器会不停地轮循。更要命的是,浏览器和服务端之间建立了一个长连接!WTF!这是多么恐怖的一件事情!只有这样的页面开32个(最新的数字我没有查,32这个数字是残存在我记忆中的)。那么,第33个页面将不能被打开。也就是说,最极端的情况,你的网站只能同时服务于32个用户。那你的网站也就没有什么存在的价值了!
分析
之所以会造成上面所说的问题,根本原因就是“单向双车道”!其实,在《计算机网络》中,这种车道叫“半双工”。所谓半双工,就是说。我有这样一条通路,这条路是可以双向通行的。但是,在同一时刻,只能有一个方向在通行。
如果服务端能主动地向客户端发送数据,那就可以解决上面所有的问题了!
WebSocket出现了
WebSocket是Html5中出现的一种新的协议,我们可以把它理解为http协议的一个补丁。WebSocket像http一样,也是一种无状态的协议,但是它可以由服务端向客户端发送消息。听起来很牛X的样子。
实现思路
说起来,实现思路其实不难。
- 服务端实现websocket服务
- 客户端向服务端发起连接,并注册回调事件
- 服务端向客户端发送消息
看一下伪代码
服务端:
声明一个websocket对象
声明连接事件
声明关闭连接的事件
声明接收到消息的事件
客户端:
function WebSocketTest()
{
if ("WebSocket" in window)
{
alert("您的浏览器支持 WebSocket!");
// 打开一个 web socket
var ws = new WebSocket("ws://localhost:12122");
ws.onopen = function()
{
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send("这是从客户端发送到服务器的消息");
console.log("这是从客户端发送到服务器的消息");
};
ws.onmessage = function (evt)
{
console.log("我收到了服务器发来的信息,内容是:" + evt.data);
};
ws.onclose = function()
{
// 关闭 websocket
alert("连接已关闭...");
};
}
else
{
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}
这样一来,我通过websocket实现了全双工通信。
git出问题了,只能先把demo代码放到百度云盘上了。
SignalRSLN\StaticPage\wsTest.html是客户端
WebSockerServerFrm是服务端
下载地址:链接:http://pan.baidu.com/s/1sk8KYCl 密码:4a1z

浙公网安备 33010602011771号