在网页上通过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调用封装
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长链接调用封装
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调用封装
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调用
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
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服务主类
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));
}
}