WebSocket分享

WebSocket_Share_Mol

@(我的分享)[websocket]

按照惯例,扯闲篇

 大家都是好几年经验的老程序员了,但是我觉得几乎没有人能把http协议说明白,当然也包括我。
 不过,正好有一个契机让我来描述一下,我的理解。这个契机就是,你们都是有驾照的人了。不管有没有真正地上过路,科目一的知识还是记得的吧。忘了?没关系。我要说的其实只有车道的概念。

遥想http

 我们都知道,http是一种协议,就像tcp/ip一样。而且它是一种“无状态”的协议。它还有一个特点,就是每次http请求都对应并且唯一对应一个响应。它看起来像是这样:
Alt text
 我们把浏览器和服务端之间的“通路”看成一条洗车走的公路,我们就会发现,这条路是“双向单车道”,尽管科目一里面没有这种路。其实你只要想一下你家门口的那条很窄的土路就可以了。在某一个特定的时刻,这条路上只能有一个方向是可以通行的。比如嚣张哥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的样子。

实现思路

 说起来,实现思路其实不难。

  1. 服务端实现websocket服务
  2. 客户端向服务端发起连接,并注册回调事件
  3. 服务端向客户端发送消息
     看一下伪代码
    服务端:
声明一个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

posted @ 2017-09-12 09:11  飞荷扬菊  阅读(167)  评论(0)    收藏  举报