Web應用程式通常有一些耗時的操作,但有些操作耗時不是很長,一分鐘之內能完成。如果採用後臺任務佇列去非同步處理,這樣的使用者不能實時看到後臺處理的情況。倘若使用者觸發操作後,Web頁面能夠實時看到後臺處理的進度,並且返回實時的狀態,使用者等待起來是不是感覺非常棒。下面是一個HTML5+ASP.NET MVC實現的示例。
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);
}
}