HTML5示例之WebSocket

known發表於2018-01-19

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);
    }
}

3.執行效果

HTML5示例之WebSocket

相關文章