在网页上通过JavaScript调用本地程序,兼容IE8/9/10/11、Opera、Chrome、Safari、Firefox等所有浏览器,在做Web开发时经常会遇到需要调用本地的一些exe或者dll程序,比如身份证读卡器,打印机等设备。

传统方式通过Activex控件,但这种方式只针对IE浏览器,并且在IE11之后Microsoft Edge浏览器中,微软也不在支持Activex控件了。因此想到了利用Websocket以及Http长链接方式来实现网页对本地资源程序的调用。

通过c#创建一个winform的客户端程序开启Websocket以及Http请求监听程序,在js中判断浏览器支持Websocket,则通过Websocket与本地程序通信,如果浏览器不支持Websocket(比如IE8/9)则自动采用Http长链接实现通信。

支持功能:心跳检测,断开重连,客户端版本检测,在一个页面可同时给客户端发送多个请求任务。每一次SocketCall调用都会生成一个唯一的任务Id,在断开重连等情况下能保证任务不丢失。

js调用示例

SocketCall(path, sendMsg, function (data) {

                //客户端返回回调方法
                console.log("返回结果:" + JSON.stringify(data));

            }, function (msg, isHeart) {
                
                //调用消息的输出,比如心跳检测,错误消息
                console.log(msg);
            });

 

js代码-自动识别通信方式

 

/* 
* 本地资源调用封装
* url:            调用地址 不需要主机和端口,例如:/yb
* data:            输入参数
* callback:        成功回调函数,函数签名为  function(data), data参数为服务端返回对象{"Code":1,"Msg":"","Data":null}
* output:        日志输出函数,函数签名为  function(msg,isHeart), msg输出字符串  isHeart是否心跳,重试消息
*/
function SocketCall(url, data, callback, output) {
    //输入参数json序列号
    if (typeof data == "object" && typeof (data) != null) {
        data = JSON.stringify(data);
    }
    //发送请求
    if ('WebSocket' in window) {
        return new WSocket(url, data, callback, output);
    } else {
        return new HttpSocket(url, data, callback, output);
    }
}

  

js代码-websocket调用封装

javascript调用本地播放器 js调用本地dll_js调用本地exe程序

javascript调用本地播放器 js调用本地dll_javascript调用本地java_02

1 /* 
  2 * webscoket调用封装
  3 * url:            调用地址
  4 * data:            输入参数
  5 * callback:        成功回调函数,函数签名为  function(data), data参数为服务端返回对象{"Code":1,"Msg":"","Data":null}
  6 * output:        日志输出函数,函数签名为  function(msg,isHeart), msg输出字符串  isHeart是否心跳,重试消息
  7 */
  8 var WSocket = function (url, data, callback, output) {
  9     this.url = url;
 10     this.data = data;
 11     this.callback = callback;
 12     this.output = output;
 13     this.outMsg = function (msg, isHeart) {
 14         if (!isHeart) isHeart = false;
 15         if (this.output) {
 16             this.output(msg, isHeart);
 17         }
 18         if (!isHeart) {
 19             console.log(msg);
 20         }
 21     };
 22     if (this.data == "HeartBeat" || this.data == "TaskIsExist") {
 23         this.outMsg("发送内容不能是关键字:HeartBeat或TaskIsExist");
 24         return;
 25     }
 26     //初始化
 27     this.taskId = GetTaskId();//任务Id
 28     this.url = "ws://" + SocketHost + GetUrlQueryString(this.url, this.taskId);//连接地址    
 29     this.IsComplate = false;//任务是否已完成
 30     this.lockReconnect = false;//是否锁定重连
 31     this.rcTimeoutObj = null;//重连定时器
 32     this.heartCheck = new WSHeartCheck(this);//心跳检测对象
 33     this.webSocket = null;//webSocket链接对象
 34     this.taskIsSend = false;//任务请求是否已发送
 35     this.IsUpgrade = false;//当前是否版本升级
 36 
 37     this.Open = function () {//连接
 38         this.webSocket = new WebSocket(this.url);
 39         this.webSocket.SrcWSocketSelf = this;
 40         if (!window.WSocketObjs) {
 41             window.WSocketObjs = new Array();
 42         }
 43         window.WSocketObjs.push(this);
 44         //连接成功建立的回调方法
 45         this.webSocket.onopen = function (event) {
 46             this.SrcWSocketSelf.IsUpgrade = false;
 47             RemoveAlertClientRunMsg();
 48             //心跳检测
 49             this.SrcWSocketSelf.heartCheck.reset().start();
 50             //发送任务数据
 51             if (!this.SrcWSocketSelf.taskIsSend) {
 52                 this.SrcWSocketSelf.webSocket.send(this.SrcWSocketSelf.data);
 53                 this.SrcWSocketSelf.taskIsSend = true;
 54                 this.SrcWSocketSelf.outMsg("ws发送任务成功,等待返回...");
 55             } else {
 56                 //判断任务是否存在
 57                 this.SrcWSocketSelf.webSocket.send("TaskIsExist");
 58             }
 59         };
 60         //接收到消息的回调方法
 61         this.webSocket.onmessage = function (event) {
 62             var resModel = JSON.parse(event.data);
 63             //处理消息
 64             if (resModel.Code == 8) {
 65                 this.SrcWSocketSelf.outMsg("ws接收心跳响应:" + event.data, true);
 66                 this.SrcWSocketSelf.heartCheck.reset().start();//重置并开始下一个心跳检测
 67             } else if (resModel.Code == 2) {//继续等待
 68                 this.SrcWSocketSelf.outMsg("重连成功,继续等待返回...");
 69             } else if (resModel.Code == 3) {//版本升级等待
 70                 this.SrcWSocketSelf.outMsg("客户端版本升级中,请等待...");
 71                 this.SrcWSocketSelf.taskIsSend = false;
 72                 this.SrcWSocketSelf.IsUpgrade = true;
 73                 this.SrcWSocketSelf.Reconnect();//启动重连
 74             } else if (this.SrcWSocketSelf.callback) {
 75                 this.SrcWSocketSelf.IsComplate = true;
 76                 this.SrcWSocketSelf.Close();
 77                 this.SrcWSocketSelf.callback(resModel);
 78             } else {
 79                 this.SrcWSocketSelf.IsComplate = true;
 80                 this.SrcWSocketSelf.Close();
 81                 this.SrcWSocketSelf.outMsg("ws接收数据:" + event.data);
 82             }
 83         };
 84         //连接发生错误的回调方法
 85         this.webSocket.onerror = function (event) {
 86             //启动应用
 87             if (this.SrcWSocketSelf.IsUpgrade != true) {
 88                 StartLocalExe();
 89             }
 90             //重新链接
 91             this.SrcWSocketSelf.Reconnect();
 92         };
 93         //连接关闭的回调方法
 94         this.webSocket.onclose = function (event) {
 95             this.SrcWSocketSelf.outMsg("ws连接已关闭", true);
 96             this.SrcWSocketSelf.heartCheck.reset();//心跳检测
 97             this.SrcWSocketSelf.Reconnect();
 98         };
 99     };
100     this.Open();
101 
102     //以下定义公共方法
103     this.Reconnect = function () {//重连
104         if (this.lockReconnect) {
105             return;
106         };
107         if (this.IsComplate) {
108             return;
109         }
110         var self = this;
111         this.lockReconnect = true;
112         this.rcTimeoutObj && clearTimeout(this.rcTimeoutObj);
113         this.rcTimeoutObj = setTimeout(function () {
114             self.lockReconnect = false;
115             self.outMsg('ws开始重试...', true);
116             self.Open();
117         }, 1000);
118     };
119     this.SendData = function (data) {//发送数据
120         if (this.webSocket == null) {
121             return "链接未初始化!";
122         }
123         if (this.webSocket.readyState != 1) {
124             return "链接不是正常状态";
125         }
126         this.webSocket.send(data);
127         return "";
128     };
129     this.Close = function (data) {//关闭连接
130         if (this.webSocket != null) {
131             this.heartCheck.reset();
132             this.webSocket.close();
133         }
134     };
135 };
136 
137 //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接或者HttpSocket。
138 window.onbeforeunload = function () {
139     if (window.WSocketObjs) {
140         for (var i in window.WSocketObjs) {
141             window.WSocketObjs[i].Close();
142         }
143     }
144 };
145 
146 //websocket心跳检测
147 var WSHeartCheck = function (wSocket) {
148     this.timeout = 8000;//心跳间隔 8秒
149     this.timeoutObj = null;
150     this.serverTimeoutObj = null;
151     this.wSocket = wSocket;
152 
153     this.reset = function () {//重置
154         clearTimeout(this.timeoutObj);
155         clearTimeout(this.serverTimeoutObj);
156         return this;
157     };
158 
159     this.start = function () {//开始心跳
160         var self = this;
161         this.timeoutObj && clearTimeout(this.timeoutObj);
162         this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
163         this.timeoutObj = setTimeout(function () {
164             //这里发送一个心跳,后端收到后,返回一个心跳消息,onmessage拿到返回的心跳就说明连接正常
165             self.wSocket.SendData("HeartBeat");
166             self.serverTimeoutObj = setTimeout(function () { // 如果超过5秒还没重置,说明链接断开了
167                 self.wSocket.Close();//onclose会执行reconnect
168             }, 5000);
169         }, this.timeout);
170     };
171 }

webscoket调用封装

 

js代码-http长链接调用封装

javascript调用本地播放器 js调用本地dll_js调用本地exe程序

javascript调用本地播放器 js调用本地dll_javascript调用本地java_02

1 /* 
  2 * http调用封装
  3 * url:            调用地址
  4 * data:            输入参数
  5 * callback:        成功回调函数,函数签名为  function(data), data参数为服务端返回对象{"Code":1,"Msg":"","Data":null}
  6 * output:        日志输出函数,函数签名为  function(msg,isHeart), msg输出字符串  isHeart是否心跳,重试消息
  7 */
  8 var HttpSocket = function (url, data, callback, output) {
  9     this.url = url;
 10     this.data = data;
 11     this.callback = callback;
 12     this.output = output;
 13     this.taskId = GetTaskId();//任务Id
 14     this.url = "http://" + SocketHost + GetUrlQueryString(this.url, this.taskId);
 15     this.taskIsSend = false;//任务请求是否已发送
 16     this.IsComplate = false;//任务是否已完成
 17     this.lockReconnect = false;//是否锁定重连
 18     this.rcTimeoutObj = null;//重连定时器
 19     this.IsUpgrade = false;//当前是否版本升级
 20     this.outMsg = function (msg, isHeart) {
 21         if (!isHeart) isHeart = false;
 22         if (this.output) {
 23             this.output(msg, isHeart);
 24         }
 25         if (!isHeart) {
 26             console.log(msg);
 27         }
 28     };
 29     //发送任务请求
 30     this.Open = function () {
 31         if (this.taskIsSend) {
 32             return;
 33         }
 34         WAjax.post(this.url, this.data, function (resData, state) {
 35             //请求成功
 36             RemoveAlertClientRunMsg();
 37             state.outMsg("htp发送任务成功,等待返回...");
 38             state.taskIsSend = true;
 39             state.IsUpgrade = false;
 40             if (resData.Code == 2) {//开始获取任务结果
 41                 state.IsComplate = false;
 42                 state.GetTaskReslut();
 43             } else if (resData.Code == 3) {//版本升级等待
 44                 state.outMsg("客户端版本升级中,请等待...");
 45                 state.taskIsSend = false;
 46                 state.IsUpgrade = true;
 47                 //重新发送
 48                 state.Reconnect();
 49             } else {
 50                 //返回结果
 51                 state.IsComplate = true;
 52                 if (state.callback) {
 53                     state.callback(resData);
 54                 }
 55             }
 56         }, function (err, state) {//ajax调用失败
 57             state.outMsg("htp发送任务失败:" + err, true);
 58             //重新发送
 59             state.Reconnect();
 60         }, this);
 61     };
 62     this.Open();
 63 
 64     //重新发送
 65     this.Reconnect = function () {
 66         if (this.lockReconnect) {
 67             return;
 68         };
 69         if (this.IsComplate) {
 70             return;
 71         }
 72         var self = this;
 73         this.lockReconnect = true;
 74         this.rcTimeoutObj && clearTimeout(this.rcTimeoutObj);
 75         this.rcTimeoutObj = setTimeout(function () {
 76             self.lockReconnect = false;
 77             self.outMsg('htp开始重试...', true);
 78             self.Open();
 79         }, 1000);
 80     };
 81 
 82     //获取任务结果
 83     this.GetTaskReslut = function () {
 84         //地址
 85         var gUrl = "http://" + SocketHost + "/_getTaskReslut" + GetUrlQueryString("", this.taskId);
 86         //get请求
 87         WAjax.get(gUrl, function (resData, state) {
 88             state.outMsg("htp获取任务状态:" + JSON.stringify(resData), true);
 89             if (resData.Code == 2) {//继续获取        
 90                 state.IsComplate = false;
 91                 state.GetTaskReslut();
 92             } else {//返回结果
 93                 state.IsComplate = true;
 94                 if (state.callback) {
 95                     state.callback(resData);
 96                 }
 97             }
 98         }, function (err, state) {
 99             state.outMsg("htp获取任务状态失败:" + err, true);//输出错误
100             state.GetTaskReslut();//继续获取
101         }, this);
102     }
103 }

http调用封装

 

javascript调用本地播放器 js调用本地dll_js调用本地exe程序

javascript调用本地播放器 js调用本地dll_javascript调用本地java_02

1 //Ajax调用
 2 var WAjax = {
 3     get: function (url, onSuccess, onError, state) {
 4         var xhr = new XMLHttpRequest();
 5         if (window.XMLHttpRequest) {
 6             xhr = new XMLHttpRequest();
 7         } else {
 8             xhr = new ActiveXObject("Microsoft.XMLHTTP");
 9         }
10         xhr.open('GET', url, true);
11         xhr.onreadystatechange = function () {
12             if (xhr.readyState == 4) {
13                 if (xhr.status == 200) {
14                     //请求成功
15                     var model = JSON.parse(xhr.responseText);
16                     if (onSuccess) {
17                         onSuccess(model, state);
18                     }
19                 } else {
20                     if (onError) {
21                         onError("http状态码" + xhr.status, state);
22                     }
23                 }
24             }
25         }
26         xhr.onerror = function (evt) {
27             //启动应用
28             if (state.IsUpgrade != true) {
29                 StartLocalExe();
30             }
31             //此处不执行错误回调,因为在onreadystatechange里面也会执行
32         }
33         xhr.send();
34     },
35 
36     post: function (url, data, onSuccess, onError, state) {
37         var xhr = new XMLHttpRequest();
38         if (window.XMLHttpRequest) {
39             xhr = new XMLHttpRequest();
40         } else {
41             xhr = new ActiveXObject("Microsoft.XMLHTTP");
42         }
43         xhr.open('POST', url, true);
44         xhr.onreadystatechange = function () {
45             if (xhr.readyState == 4) {
46                 if (xhr.status == 200) {
47                     //请求成功
48                     var model = JSON.parse(xhr.responseText);
49                     if (onSuccess) {
50                         onSuccess(model, state);
51                     }
52                 } else {
53                     if (onError) {
54                         onError("http状态码" + xhr.status, state);
55                     }
56                 }
57             }
58         }
59         xhr.onerror = function (evt) {
60             //启动应用
61             if (state.IsUpgrade != true) {
62                 StartLocalExe();
63             }
64             //此处不执行错误回调,因为在onreadystatechange里面也会执行
65         }
66         xhr.send(data);
67     }
68 }

Ajax调用

 

javascript调用本地播放器 js调用本地dll_js调用本地exe程序

javascript调用本地播放器 js调用本地dll_javascript调用本地java_02

1 //获取任务Id
 2 function GetTaskId() {
 3     var now = new Date();
 4     var taskId = (now.getMonth() + 1).toString();
 5     taskId += now.getDate();
 6     taskId += now.getHours();
 7     taskId += now.getMinutes();
 8     taskId += now.getSeconds();
 9     taskId += now.getMilliseconds();
10     taskId += Math.random().toString().substr(3, 5);//3位随机数
11     return taskId;
12 }
13 
14 //启动本地exe
15 function StartLocalExe() {
16     var ifrm = document.getElementById("ifrm_StartLocalExe");
17     if (!ifrm) {
18         ifrm = document.createElement('iframe');
19         ifrm.id = "ifrm_StartLocalExe";
20         ifrm.src = "XhyClinicClient://";
21         ifrm.width = 0;
22         ifrm.height = 0;
23         document.body.appendChild(ifrm);
24     }
25     document.getElementById('ifrm_StartLocalExe').contentWindow.location.reload();
26 
27     //var linkTmp = document.createElement('a');
28     //linkTmp.href = 'XhyClinicClient://';
29     //linkTmp.click();
30 };
31 
32 //获取地址请求参数
33 function GetUrlQueryString(url, taskId) {
34     //参数中添加time 以兼容在Ie9以下浏览器中 相同http地址,浏览器的缓存不处理问题
35     if (url.indexOf("?") > 0) {
36         return url + "&taskId=" + taskId + "&clientVer=" + ClientVer + "&time" + new Date().getTime();
37     } else {
38         return url + "?taskId=" + taskId + "&clientVer=" + ClientVer + "&time" + new Date().getTime();
39     }
40 }

其他相关js方法

 

客户端程序

客户端采用c#,winform实现,采用.netframework4.0版本,支持xp,win7,win10等操作系统。websocket实现采用的是开源项目:websocket-sharp.dll

javascript调用本地播放器 js调用本地dll_js调用本地exe程序

javascript调用本地播放器 js调用本地dll_javascript调用本地java_02

1 /// <summary>
  2     /// WebSocket主类
  3     /// </summary>
  4     public class WSMain
  5     {
  6         #region 字段
  7 
  8         /// <summary>
  9         /// 检测客户端连接 处理线程
 10         /// </summary>
 11         private BackgroundWorker _doWork_checkClient;
 12         /// <summary>
 13         /// 检测线程 信号灯
 14         /// </summary>
 15         private AutoResetEvent _doWorkARE = new AutoResetEvent(true);
 16 
 17         private int _heartBeatSecond = 8000;
 18         /// <summary>
 19         /// 获取或设置 心跳检测时间间隔(单位毫秒)  默认值(8000毫秒)
 20         /// 此值必须与客户端设置相同值 最小值1000毫秒
 21         /// </summary>
 22         public int HeartBeatSecond
 23         {
 24             get { return _heartBeatSecond; }
 25             set
 26             {
 27                 if (value < 1000)
 28                 {
 29                     throw new Exception("心跳检测时间间隔必须大于等于1秒");
 30                 }
 31                 _heartBeatSecond = value;
 32             }
 33         }
 34         /// <summary>
 35         /// 服务器是否正在运行
 36         /// </summary>
 37         public bool IsRunning { get; private set; }
 38 
 39         /// <summary>
 40         /// WebSocketServer
 41         /// </summary>
 42         private HttpServer _httpServer;
 43         /// <summary>
 44         /// WebSocketServer对象
 45         /// </summary>
 46         public HttpServer HttpServer
 47         {
 48             get
 49             {
 50                 return _httpServer;
 51             }
 52         }
 53 
 54         #endregion
 55 
 56         /// <summary>
 57         /// 启动WebSocket
 58         /// </summary>
 59         public void Start()
 60         {
 61             _httpServer = new HttpServer(CommonInfo.WsPort);
 62             _httpServer.OnGet += _httpServer_Handler;
 63             _httpServer.OnPost += _httpServer_Handler;
 64             foreach (KeyValuePair<string, HandlerModel> item in HandlerManager.Handlers)
 65             {
 66                 _httpServer.AddWebSocketService<MyWebSocketBehavior>(string.Format("/{0}", item.Key));
 67             }
 68             _httpServer.Start();
 69 
 70             //启动检测
 71             IsRunning = true;
 72             _doWork_checkClient = new BackgroundWorker();
 73             _doWork_checkClient.DoWork += DoWorkCheckMethod;
 74             _doWork_checkClient.RunWorkerAsync();
 75         }
 76 
 77         #region Http请求处理
 78 
 79         /// <summary>
 80         /// Http请求处理
 81         /// </summary>
 82         /// <param name="sender"></param>
 83         /// <param name="e"></param>
 84         private void _httpServer_Handler(object sender, HttpRequestEventArgs e)
 85         {
 86             try
 87             {
 88                 if (string.IsNullOrWhiteSpace(e.Request.Url.AbsolutePath))
 89                 {
 90                     HttpResponse(e, new WSResModel(ResCode.Err, "请求路径不存在!"));
 91                     return;
 92                 }
 93                 string path = e.Request.Url.AbsolutePath.TrimStart('/');
 94                 string taskId = e.Request.QueryString["taskId"];
 95                 string clientVer = e.Request.QueryString["clientVer"];//客户端版本号
 96                 if (path == "TestConnect")
 97                 {
 98                     HttpResponse(e, new WSResModel(ResCode.OK));
 99                     return;
100                 }
101                 if (!string.IsNullOrWhiteSpace(clientVer) && CommonInfo.Version != clientVer)
102                 {
103                     HttpResponse(e, new WSResModel(ResCode.ClientAutoUpgrade, string.Format("请将客户端版本升级至{0}", clientVer)));
104                     Program.ExitAndStartAutoUpgrade();
105                     return;
106                 }
107                 if (string.IsNullOrWhiteSpace(taskId))
108                 {
109                     HttpResponse(e, new WSResModel(ResCode.Err, "请求参数TaskId必须!"));
110                     return;
111                 }
112                 if (path == "_getTaskReslut")
113                 {
114                     Stopwatch sw = new Stopwatch();
115                     sw.Start();
116                     GetTaskWaitReslut(taskId, e, sw);
117                 }
118                 else
119                 {
120                     BaseHandler handler = HandlerManager.CreateHandler(path);
121                     if (handler == null)
122                     {
123                         HttpResponse(e, new WSResModel(ResCode.Err, string.Format("请求路径{0}未找到对应Handler", path)));
124                         return;
125                     }
126                     //参数解析
127                     string msg = string.Empty;
128                     if (e.Request.ContentLength64 > 0)
129                     {
130                         using (System.IO.StreamReader stream = new System.IO.StreamReader(e.Request.InputStream, Encoding.UTF8))
131                         {
132                             msg = stream.ReadToEnd();
133                         }
134                     }
135                     //断开并返回客户端
136                     HttpResponse(e, new WSResModel(ResCode.Wait));
137                     //线程继续处理消息
138                     handler.Path = path;
139                     handler.TaskId = taskId;
140                     handler.ReqIsWebSocket = false;
141                     handler.InPara = msg;
142                     if (HandlerTaskManager.AddTask(handler))
143                     {
144                         handler.HandlerTask(msg);
145                     }
146                 }
147             }
148             catch (Exception ex)
149             {
150                 HttpResponse(e, new WSResModel(ResCode.Err, string.Format("服务器异常:{0}", ex.Message)));
151             }
152         }
153 
154         /// <summary>
155         /// 等待并获取任务结果
156         /// </summary>
157         /// <param name="taskId"></param>
158         /// <param name="e"></param>
159         private void GetTaskWaitReslut(string taskId, HttpRequestEventArgs e, Stopwatch sw)
160         {
161             try
162             {
163                 if (sw.ElapsedMilliseconds > HeartBeatSecond)
164                 {
165                     //超过心跳 则返回给客户端,以便下次继续请求
166                     HttpResponse(e, new WSResModel(ResCode.Wait));
167                     return;
168                 }
169                 //获取任务状态
170                 WSResModel resModel = HandlerTaskManager.GetTaskReslut(taskId);
171                 if (resModel != null)
172                 {
173                     HttpResponse(e, resModel);
174                 }
175                 else
176                 {
177                     Thread.Sleep(50);
178                     GetTaskWaitReslut(taskId, e, sw);
179                 }
180             }
181             catch (Exception ex)
182             {
183                 HttpResponse(e, new WSResModel(ResCode.Err, string.Format("服务器内部异常:{0}", ex.Message)));
184             }
185         }
186 
187         /// <summary>
188         /// Http响应客户端
189         /// </summary>
190         /// <param name="e"></param>
191         /// <param name="resMsg"></param>
192         private void HttpResponse(HttpRequestEventArgs e, WSResModel resModel)
193         {
194             try
195             {
196                 string json = CommonLibrary.MyJson.JsonSerializer(resModel);
197                 byte[] content = System.Text.Encoding.UTF8.GetBytes(json);
198 
199                 e.Response.StatusCode = (int)HttpStatusCode.OK;
200                 e.Response.AddHeader("Access-Control-Allow-Origin", "*");//允许跨域访问
201                 e.Response.ContentEncoding = Encoding.UTF8;
202                 e.Response.ContentLength64 = content.Length;
203                 e.Response.OutputStream.Write(content, 0, content.Length);
204                 e.Response.OutputStream.Close();
205                 e.Response.Close();
206             }
207             catch (Exception ex)
208             {
209                 CommonInfo.Output("Http响应客户端异常:{0}", ex.Message);
210             }
211         }
212 
213         #endregion
214 
215         /// <summary>
216         /// 后台线程方法
217         /// 主要处理客户端连接
218         /// </summary>
219         /// <param name="sender"></param>
220         /// <param name="e"></param>
221         private void DoWorkCheckMethod(object sender, DoWorkEventArgs e)
222         {
223             int timeout = HeartBeatSecond * 3;//连续3次检测无心跳 则表示客户端已死掉了
224             while (IsRunning)
225             {
226                 try
227                 {
228                     _doWorkARE.Reset();
229                     _doWorkARE.WaitOne(HeartBeatSecond);
230 
231                     //定时清理任务
232                     HandlerTaskManager.ClearTask();
233 
234                     foreach (WebSocketServiceHost host in _httpServer.WebSocketServices.Hosts)
235                     {
236                         List<IWebSocketSession> tempList = host.Sessions.Sessions.ToList();
237                         foreach (var item in tempList)
238                         {
239                             MyWebSocketBehavior handler = item as MyWebSocketBehavior;
240                             if (handler != null)
241                             {
242                                 //检测连接
243                                 if ((DateTime.Now - handler.LastRecvTime).TotalSeconds > timeout)
244                                 {
245                                     //断开客户端连接
246                                     CloseClientSocket(handler);
247                                 }
248                             }
249                         }
250                     }
251                 }
252                 catch (Exception ex)
253                 {
254                     CommonInfo.Output("检测连接异常:{0}", ex.Message);
255                 }
256             }
257         }
258 
259         /// <summary>
260         /// 关闭客户端连接
261         /// </summary>
262         /// <param name="session"></param>
263         public void CloseClientSocket(MyWebSocketBehavior handler)
264         {
265             try
266             {
267                 handler.Context.WebSocket.Close();
268             }
269             catch (Exception ex)
270             {
271                 CommonInfo.Output("断开客户端异常:{0}", ex.Message);
272             }
273         }
274 
275         /// <summary>
276         /// 退出WebSocket
277         /// </summary>
278         public void Stop()
279         {
280             //关闭WebScoket
281             IsRunning = false;
282             _doWorkARE.Set();
283             HandlerTaskManager.TaskQueue.Clear();
284             _httpServer.Stop(WebSocketSharp.CloseStatusCode.Abnormal, "服务退出");
285         }
286 
287         #region 单例对象
288 
289         private static WSMain _instance;
290         /// <summary>
291         /// 单例对象
292         /// </summary>
293         public static WSMain Instance
294         {
295             get
296             {
297                 if (_instance == null)
298                 {
299                     _instance = new WSMain();
300                 }
301                 return _instance;
302             }
303         }
304         #endregion
305     }

WebSocket服务主类

 

javascript调用本地播放器 js调用本地dll_js调用本地exe程序

javascript调用本地播放器 js调用本地dll_javascript调用本地java_02

1 /// <summary>
  2     /// WebSocket消息处理类
  3     /// </summary>
  4     public class MyWebSocketBehavior : WebSocketBehavior
  5     {
  6         #region 字段
  7 
  8         /// <summary>
  9         /// 最后接受数据时间
 10         /// </summary>
 11         public DateTime LastRecvTime = DateTime.Now;
 12 
 13         /// <summary>
 14         /// 任务Id
 15         /// </summary>
 16         public string TaskId
 17         {
 18             get
 19             {
 20                 return this.Context.QueryString["taskId"];
 21             }
 22         }
 23 
 24         /// <summary>
 25         /// 客户端版本号
 26         /// </summary>
 27         public string ClientVer
 28         {
 29             get
 30             {
 31                 return this.Context.QueryString["clientVer"];
 32             }
 33         }
 34 
 35         /// <summary>
 36         /// 请求路径
 37         /// </summary>
 38         public string PathName
 39         {
 40             get
 41             {
 42                 return this.Context.RequestUri.AbsolutePath.TrimStart('/');
 43             }
 44         }
 45 
 46         /// <summary>
 47         /// 连接数发生变化事件
 48         /// </summary>
 49         public static event Action<MyWebSocketBehavior> ConnectCountChange;
 50 
 51         #endregion
 52 
 53         /// <summary>
 54         /// 新连接
 55         /// </summary>
 56         protected override void OnOpen()
 57         {
 58             if (ConnectCountChange != null)
 59             {
 60                 ConnectCountChange(this);
 61             }
 62             if (!string.IsNullOrWhiteSpace(this.ClientVer) && CommonInfo.Version != this.ClientVer)
 63             {
 64                 SendAndClose(new WSResModel(ResCode.ClientAutoUpgrade, string.Format("请将客户端版本升级至{0}", this.ClientVer)));
 65                 Program.ExitAndStartAutoUpgrade();
 66             }
 67             //CommonInfo.Output("新连接{0}", this.TaskId);
 68         }
 69 
 70         /// <summary>
 71         /// 新消息
 72         /// </summary>
 73         /// <param name="e"></param>
 74         protected override void OnMessage(WebSocketSharp.MessageEventArgs e)
 75         {
 76             try
 77             {
 78                 LastRecvTime = DateTime.Now;
 79                 if (e.IsText)
 80                 {
 81                     switch (e.Data)
 82                     {
 83                         case "HeartBeat":
 84                             this.Send(CommonLibrary.MyJson.JsonSerializer(new WSResModel(ResCode.HeartBeatRes)));
 85                             return;
 86                         case "TaskIsExist":
 87                             if (HandlerTaskManager.ContainsTask(this.TaskId))
 88                             {
 89                                 this.Send(CommonLibrary.MyJson.JsonSerializer(new WSResModel(ResCode.Wait)));
 90                             }
 91                             else
 92                             {
 93                                 SendAndClose(new WSResModel(ResCode.Err, "任务不存在!"));
 94                             }
 95                             return;
 96                     }
 97                 }
 98                 BaseHandler handler = HandlerManager.CreateHandler(PathName);
 99                 if (handler == null)
100                 {
101                     SendAndClose(new WSResModel(ResCode.Err, string.Format("请求路径{0}未找到对应Handler", PathName)));
102                     return;
103                 }
104                 handler.Path = this.PathName;
105                 handler.TaskId = this.TaskId;
106                 handler.ReqIsWebSocket = true;
107                 handler.InPara = e.Data;
108                 //添加任务并执行
109                 if (HandlerTaskManager.AddTask(handler))
110                 {
111                     ThreadPool.QueueUserWorkItem((state) =>
112                     {
113                         BaseHandler bh = state as BaseHandler;
114                         try
115                         {
116                             bh.HandlerTask(bh.InPara);
117                         }
118                         catch (Exception ex)
119                         {
120                             bh.ReturnToClient(new WSResModel(ResCode.Err, ex.Message));
121                         }
122                     }, handler);
123                 }
124             }
125             catch (Exception ex)
126             {
127                 CommonInfo.Output("WS处理消息异常:{0}", ex.Message);
128                 SendAndClose(new WSResModel(ResCode.Err, string.Format("服务器WS处理消息异常", ex.Message)));
129             }
130         }
131 
132         /// <summary>
133         /// 错误
134         /// </summary>
135         /// <param name="e"></param>
136         protected override void OnError(WebSocketSharp.ErrorEventArgs e)
137         {
138             if (e.Exception != null)
139             {
140                 CommonInfo.Output("连接{0}错误:{1}", this.TaskId, e.Exception.GetBaseException().Message);
141             }
142             else
143             {
144                 CommonInfo.Output("连接{0}错误:{1}", this.TaskId, e.Message);
145             }
146         }
147 
148         /// <summary>
149         /// 关闭
150         /// </summary>
151         /// <param name="e"></param>
152         protected override void OnClose(WebSocketSharp.CloseEventArgs e)
153         {
154             if (ConnectCountChange != null)
155             {
156                 ConnectCountChange(this);
157             }
158             //CommonInfo.Output("断开连接{0}", this.TaskId);
159         }
160 
161         /// <summary>
162         /// 结果返回给客户端并断开链接
163         /// </summary>
164         /// <param name="msg"></param>
165         public void SendAndClose(WSResModel resModel)
166         {
167             try
168             {
169                 string json = CommonLibrary.MyJson.JsonSerializer(resModel);
170                 this.Send(json);
171                 try
172                 {
173                     this.Context.WebSocket.Close();
174                 }
175                 catch (Exception ex) { }
176             }
177             catch (Exception ex)
178             {
179                 CommonInfo.Output("发送消息异常:{0}", ex.Message);
180             }
181         }
182     }

WebSocket消息处理类

 

在c#中,自定义消息处理类,只需自己新建一个类,然后继承至BaseHandler,并实现HandlerTask方法,比如:

/// <summary>
    /// 消息处理器
    /// </summary>
    [HandlerAttribute("yb")]//对应websocket或者http请求路径,比如:ws:127.0.0.1:3964/yb   或者: http://127.0.0.1:3964/yb
    public class YBHandler : BaseHandler
    {
        /// <summary>
        /// 处理消息
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        public override void HandlerTask(string msg)
        {
            //Thread.Sleep(20000);
            FrmTest frm = new FrmTest();
            frm.TopMost = true;
            frm.ShowDialog();
            //在子类中通过调用父类的ReturnToClient方法将消息json对象返回给网页
            this.ReturnToClient(new WSResModel(ResCode.OK, DateTime.Now.ToString("HH:mm:ss") + "OK:" + msg));
        }
    }