让web socket"兼容"各个浏览器

建立了Socket服务端,服务器数据有变动,想要推送给客户端,如果是C/S模式,使用Socket自然没问题;如果是B/S呢,没有Socket怎么办?

html5支持WebSocket,这是一个不错的改善。

想起没有顺丰、韵达、各种通等之类快递的日子,你以为远方的朋友会给你邮寄包裹——虽然不会有人这样做——然后你就翻山越岭天天去邮局问,“有没有我的包裹啊”,运气好的话,有,你领回去了,运气不好,对不起接着坚持来问吧。于是,跑啊跑啊,累个半死不活。

不过就你自己累,邮局无所谓,反正不用给你送货上门。

但架不住人多啊,更多的像你一样的人有你那样的想法了,跑邮局,人多邮局就出问题了,每天只回答有没有包裹这个问题都忙到嘴没工夫闭上的地步,无法进行日常的发信和拍电报业务了。

于是邮局崩溃了。

这时,快递产生了。

他们负责送货上门。住在城市中的人幸福了,不用跑邮局了。不过住在乡下的人还是郁闷,他们以为快递会平等对待给他们送货上门,也不跑邮局了,可等来等去,等到了一句话:乡下交通不便,恕不配送。。

kao kao kao

 

html5 WebSocket同样遇到了这样的尴尬。对IE,它只兼容10以及以上的版本(其它浏览器不考虑,IE是大户)。对于6789这些依旧没能淘汰掉的老古董,开发人员只能采用别的方式解决。

有人说Nodejs可以。

不错,确实可以。

不过我想说的是,Nodejs的socket.io应该是判断了浏览器的兼容性之后做出的选择吧?支持web socket的采用websocket,不支持的采用别的方式(跑邮局)?

工作也这么多年了,script一直是我的弱项。。。。

虽然照猫画虎使用nodejs及socket.io写出了同样的功能,但心里总是没底。。。打脸-ing

我说nodejs没别的意思,只是我不会。。。

***********************************************************分割线***********************************************************

之前有看园子里的文章,知道使用Flash的socket可以解决兼容性的问题,但由于flex已经好几年没碰了,就没心思去搞。

使用nodejs处理之后由于底气不足不敢在线上服务器用啊,于是转头copy别人的代码使用flash去解决socket问题,搞来搞去园友的文章不是抄别人的就是给出的flash是只适合自己用的,总也调试不通,干脆自己搞,也就是重拾一下as和flex而已。

不多说废话了(打脸-ing),上代码吧。

***********************************************************分割线***********************************************************

首先,创建Socket服务端,c#代码(我是使用Windows服务作Socket服务端,至于别的方式请自行脑补):

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Diagnostics;
  6 using System.Linq;
  7 using System.Net;
  8 using System.Net.Sockets;
  9 using System.Reflection;
 10 using System.ServiceProcess;
 11 using System.Text;
 12 using log4net;
 13 
 14 namespace YourNameSpace
 15 {
 16     partial class FlashSocket : ServiceBase
 17     {
 18         private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
 19         private static Socket server;
 20 
 21         private static Socket handSocket;
 22 
 23         private static string message = "testset";
 24         /// <summary>
 25         /// 需要维护的一个Socket Client集合
 26         /// </summary>
 27         private static List<Socket> Clients = new List<Socket>();
 28         public FlashSocket()
 29         {
 30             InitializeComponent();
 31         }
 32 
 33         protected override void OnStart(string[] args)
 34         {
 35             // TODO:  在此处添加代码以启动服务。
 36             CreateSocketServer();
 37             System.Timers.Timer tmr = new System.Timers.Timer();
 38             tmr.Interval = 1000 * 3;
 39             tmr.Elapsed += (sender, e) =>
 40             {
 41                 DoWork();
 42             };
 43             tmr.Start();
 44         }
 45 
 46 
 47         protected override void OnStop()
 48         {
 49             // TODO:  在此处添加代码以执行停止服务所需的关闭操作。
 50         }
 51 
 52         #region 创建Socket服务端
 53 
 54         private static void CreateSocketServer()
 55         {
 56             AllowDomain ad = new AllowDomain();
 57             server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 58             server.Bind(new IPEndPoint(IPAddress.Parse("192.168.5.134"), 2013));
 59             server.Listen(int.MaxValue);
 60             server.BeginAccept(InsertClientToCollection, server);
 61         }
 62         #endregion
 63         
 64         #region 有新客户端访问的时候添加进来
 65         /// <summary>
 66         /// 有新客户端访问的时候添加到客户端集合中
 67         /// </summary>
 68         /// <param name="ar"></param>
 69         private static void InsertClientToCollection(IAsyncResult ar)
 70         {
 71             try
 72             {
 73                 var socket = ar.AsyncState as Socket;
 74                 if (socket != null)
 75                 {
 76                     Clients.Add(socket.EndAccept(ar));
 77                     server.BeginAccept(InsertClientToCollection, server);
 78                     Logger.InfoFormat("新用户连接{0}", socket.LocalEndPoint);
 79                 }
 80             }
 81             catch (Exception ex)
 82             {
 83                 Logger.Error(ex);
 84             }
 85         }
 86 
 87         #endregion
 88         #region
 89         private void DoWork()
 90         {
 91             message = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\0";
 92             try
 93             {
 94                 if (Clients.Count > 0)
 95                 {
 96                     var count = Clients.Count;
 97                     for (int i = 0; i < count; )
 98                     {
 99                         if (i < Clients.Count)
100                         {
101                             try
102                             {
103                                 Clients[i].Send(Encoding.UTF8.GetBytes(message));
104                             }
105                             catch (Exception)
106                             {
107                                 Clients.RemoveAt(i);
108                                 i--;
109                             }
110                         }
111                         i++;
112                     }
113                 }
114                 if (Clients.Count > 0)
115                     Logger.InfoFormat("当前客户端总数:{0}", Clients.Count);
116             }
117             catch (Exception ex)
118             {
119                 Logger.Error(ex);
120             }
121 
122         }
123         #endregion
124 
125     }
126 }
查看代码

其中CreateSocketServer方法内有一句“AllowDomain ad = new AllowDomain();”,这个是解决flash安全沙箱跨域问题的,如果没有这句,flash读取socket数据的时候会报安全沙箱冲突。其实就是暴露Socket Server所在服务器的843端口给flash,以供握手(个人理解)。

代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Net.Sockets;
 5 using System.Text;
 6 
 7 namespace YourNameSpace
 8 {
 9     public class AllowDomain
10     {
11         private TcpListener Server;
12         private AsyncCallback callback;
13         private bool islisten = false;
14 
15         public AllowDomain()
16         {
17             this.Server = new TcpListener(843);
18             this.Server.Start();
19             this.callback = new AsyncCallback(this.OnConnectionEvent);
20             this.islisten = true;
21             this.Server.BeginAcceptSocket(this.callback, null);
22         }
23 
24         public void Close()
25         {
26             this.islisten = false;
27             this.Server.Stop();
28         }
29 
30         private void OnConnectionEvent(IAsyncResult syn)
31     {
32         if (this.islisten)
33         {
34             SocketError error;
35             Socket conn = this.Server.EndAcceptSocket(syn);
36             //conn.Send(PolicyFile.Policys);
37             
38             byte[] buffer = new byte[1024];
39             int len = conn.Receive(buffer, 0, 1024, SocketFlags.None, out error);
40             if (error == SocketError.Success)
41             {
42                 string s = Encoding.Default.GetString(buffer, 0, len);
43                 if (s == "<policy-file-request/>\0")
44                 {
45                     buffer = Encoding.Default.GetBytes("<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"2013\"/></cross-domain-policy>\0");
46                     conn.Send(buffer);
47                 }
48             }
49             conn.Close();            
50             if (this.islisten)
51                 this.Server.BeginAcceptSocket(this.OnConnectionEvent, null);
52         }
53     }
54     }
55 }
跨域

 下面是Flash代码,AS3:

 1 package
 2 {
 3     import flash.display.Sprite;
 4     import flash.events.Event;
 5     import flash.events.ProgressEvent;
 6     import flash.external.ExternalInterface;
 7     import flash.net.Socket;
 8     import flash.system.Security;
 9     import flash.utils.ByteArray;
10     
11     public class PushMessageLess extends Sprite
12     {
13         private var socket:Socket;
14         private var msg:String="";
15         protected function CreateSocket():void
16         {
17             Security.allowDomain("*");
18             Security.loadPolicyFile("xmlsocket://192.168.5.134:843");
19             
20             // TODO Auto-generated method stub
21             socket=new Socket();
22             socket.connect("192.168.5.134",2013);
23             socket.addEventListener(ProgressEvent.SOCKET_DATA,receivedMessage);
24         }
25         
26         private function socketConnected(event:Event):void{
27         }
28         private function receivedMessage(e:ProgressEvent):void{
29             var message:String="";
30             while(socket.bytesAvailable){
31                 message+=socket.readMultiByte(socket.bytesAvailable,"utf8");
32             }
33             msg=message;
34             if(message.length>0){
35                 callJs(message);
36             }
37         }
38         
39         private function callJs(m:String):void{
40             ExternalInterface.call("callFlexFunction",m);
41         }
42         public function PushMessageLess()
43         {
44             if(stage){
45                 CreateSocket();
46             }else
47             {
48                 addEventListener(Event.ADDED_TO_STAGE,CreateSocket);
49             }
50         }
51     }
52 }
Flash代码,注意,请修改后使用

使用Flash Builder编译成swf文件PushMessageLess.swf(其实Less没有别的意思,是我原来用的是Flex项目,编译后把sdk中mx的部分东西编译进去了,比较大,有280k+,这个只有2k,可以接受)。

这个文件名会在后面代码中用到,拿来主义者切记:用的时候改名字。

下面是html代码:

 1 <html>
 2 <head>
 3     <title>测试Flash socket兼容IE6,7,8</title>
 4     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 5     <script src="swfobject.js"></script>
 6 </head>
 7 
 8 <body>
 9 <script type="text/javascript">
10     function callFlexFunction(msg) {
11         if (msg != null) {
12             document.getElementById("msgContainer").innerHTML = msg;
13         }
14     }
15     var flashvars = false;
16     var params = {};
17     params.allowScriptAccess = "always";
18     var attributes = {};
19     //ID,也就是swf的ID,这个ID很重要,通过它调用flex的方法 
20     attributes.id = "PushMessageLess";
21 
22     swfobject.embedSWF("PushMessageLess.swf?"+Math.random(), "PushMessageLess", "0", "0", "9.0.0","",flashvars,params,attributes);
23 </script>
24 <div id="PushMessageLess" style="display: none;"></div>
25 <div id="msgContainer"></div>
26 </body>
27 </html>
调用

所需做的只是把上面生成的swf文件放到html同级目录下(当然,不放同级目录也可以,注意下html中的路径就可以了)。

还有一个就是html页面中引用的一个swfObject.js,地址在这里

swfObject.js

就这么多了,希望各位少走弯路。

posted @ 2015-11-30 15:32  潘豹  阅读(1014)  评论(0编辑  收藏  举报