Web应用程序通常有一些耗时的操作,但耗时不是很长,一分钟之内能完成的。如果采用后台任务队列去异步处理,这样的用户不能实时看到后台处理的情况。倘若用户触发操作后,Web页面能够实时看到后台处理的进度,并且返回实时的状态,用户等待起来是不是感觉非常棒。
1.前端页面和脚本
页面包含一个文本框和一个【提交】按钮,文本框输入后台要返回的消息数。
<h2>WebSocket</h2><div class="form-inline"> ???<div class="form-group"> ???????<label for="count">消息数</label> ???????<input type="text" class="form-control" id="count" placeholder="服务端返回的消息数"> ???</div> ???<button class="btn btn-primary" onclick="wsTest()">提交</button></div><blockquote> ???<ol id="msg"></ol></blockquote>
WebSocket的api很简单,如下示例封装了一个简单的操作。
var WsUtil = { ???msg: document.getElementById('msg'), ???ws: null, ???connect: function (url, callback) { ???????var _this = this; ???????_this.msg.innerHTML = ''; ???????_this.appendMsg('正在连接......', '#00f'); ???????_this.ws = new WebSocket(url); ???????_this.ws.onopen = function () { ???????????_this.appendMsg('客户端已连接', '#00f'); ???????????if (callback) { ???????????????callback(_this.ws); ???????????} ???????} ???????_this.ws.onmessage = function (evt) { ???????????_this.appendMsg(evt.data); ???????} ???????_this.ws.onclose = function () { ???????????_this.appendMsg('客户端已断开连接', '#00f'); ???????} ???????_this.ws.onerror = function (evt) { ???????????_this.appendMsg(evt.data, '#f00'); ???????} ???}, ???close: function () { ???????if (this.ws) { ???????????this.ws.close(); ???????????this.ws = null; ???????} ???}, ???appendMsg: function (message, color) { ???????var li = document.createElement('li'); ???????li.style.color = color || '#000'; ???????li.innerHTML = message; ???????msg.appendChild(li); ???}}function wsTest() { ???var count = document.getElementById('count').value; ???var url = 'ws://localhost:90/html5/wstask?count=' + count; ???WsUtil.connect(url, function (ws) { ???????ws.send('test'); ???});}
2.ASP.NET MVC后端实现WebSocket请求
ASP.NET MVC控制器
public class Html5Controller : Controller{ ???public void WsTask() ???{ ???????HttpContext.AcceptWebSocketRequest(ctx => ???????{ ???????????int.TryParse(ctx.QueryString["count"], out int count); ???????????return WebSocketManager.RunTask(ctx, wsm => ???????????{ ???????????????for (int i = 0; i < count; i++) ???????????????{ ???????????????????var message = string.Format("{0:yyyyMMdd HH:mm:ss} 消息{1}", DateTime.Now, i + 1); ???????????????????wsm.SendMessageAsync(message); ???????????????????Thread.Sleep(1000); ???????????????} ???????????}); ???????}); ???}}
这里封装了一个WebSocket管理者类。
public class WebSocketManager{ ???private WebSocket socket; ???public WebSocketManager() ???{ ???} ???public WebSocketManager(WebSocket socket) ???{ ???????this.socket = socket; ???} ???public static async Task RunTask(AspNetWebSocketContext context, Action<WebSocketManager> action) ???{ ???????var socket = context.WebSocket; ???????if (socket.State == WebSocketState.Open) ???????{ ???????????var wsm = new WebSocketManager(socket); ???????????try ???????????{ ???????????????action(wsm); ???????????} ???????????catch (Exception ex) ???????????{ ???????????????await wsm.SendMessageAsync(ex.Message); ???????????} ???????} ???} ???public Task SendMessageAsync(string message) ???{ ???????var content = new ArraySegment<byte>(Encoding.UTF8.GetBytes(message)); ???????return socket.SendAsync(content, WebSocketMessageType.Text, true, CancellationToken.None); ???}}