首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET ServerPush 長連接實作:模擬伺服器主動推送機制

     編輯:ASP.NET 基礎

ServerPush 核心概念

HTTP 協定本質是「請求-回應」模式,瀏覽器發起請求,伺服器回應。ServerPush(長連接)透過保持連接開啟,模擬伺服器「主動推送」資料給客戶端。

實作架構:雙頁面訊息交換系統

  • 資料儲存:訊息暫存於資料庫 T_Msgs 表
  • 發送端:寫入訊息至資料庫
  • 接收端:長輪詢查詢資料庫,取得訊息後立即刪除
  • 前端:jQuery AJAX 循環請求,實現準即時效果

後端處理程式完整實作

public class ServerPush1 : IHttpHandler 
{
    public void ProcessRequest(HttpContext context) 
    {
        context.Response.ContentType = "application/json";
        string action = context.Request["action"];
        
        if (action == "send") // 發送訊息
        {
            string me = context.Request["me"];
            string toUserName = context.Request["toUserName"];
            string msg = context.Request["msg"];
            
            SQLHpler.ExecuteNonQuery(
                "INSERT INTO T_Msgs(FromUserName,ToUserName,Msg) VALUES(@FromUserName,@ToUserName,@Msg)",
                new SqlParameter("@FromUserName", me),
                new SqlParameter("@ToUserName", toUserName),
                new SqlParameter("@Msg", msg)
            );
            context.Response.Write(new JavaScriptSerializer().Serialize(new { Status = "ok" }));
        }
        else if (action == "receive") // 長連接接收
        {
            string me = context.Request["me"];
            
            while (true) // 持續輪詢
            {
                DataTable dt = SQLHpler.ExecuteQuery(
                    "SELECT TOP 1 * FROM T_Msgs WHERE ToUserName=@ToUserName",
                    new SqlParameter("@ToUserName", me)
                );
                
                if (dt.Rows.Count <= 0) 
                {
                    Thread.Sleep(500); // 降低資料庫壓力與 CPU 佔用
                    continue;
                }
                else 
                {
                    DataRow row = dt.Rows[0];
                    long id = (long)row["Id"];
                    string fromUserName = (string)row["FromUserName"];
                    string msg = (string)row["Msg"];
                    
                    // 必須刪除,否則陷入死循環重複推送
                    SQLHpler.ExecuteNonQuery(
                        "DELETE FROM T_Msgs WHERE Id=@Id",
                        new SqlParameter("@Id", id)
                    );
                    
                    var data = new { FromUserName = fromUserName, Msg = msg, Id = id };
                    string json = new JavaScriptSerializer().Serialize(data);
                    context.Response.Write(json);
                    break; // 回應後結束此次長連接
                }
            }
        }
        else 
        {
            throw new Exception("action錯誤");
        }
    }
}

前端頁面完整實作

<!DOCTYPE html>
<html xmlns="https://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>ServerPush Demo</title>
    <script src="jquery-1.8.3.min.js"></script>
    <script type="text/javascript">
        var rev = function () {
            var mine = $('#me').val();
            $.ajax({
                type: 'post',
                url: 'serverPush1.ashx',
                data: { action: 'receive', me: mine },
                success: function (data) {
                    $('#ulMsg').append($('<li>' + data.FromUserName + '對我說:' + data.Msg + '</li>'));
                    rev(); // 收到訊息後立即發起下一次請求
                },
                error: function () {
                    rev(); // 網路失敗也重試
                }
            });
        };

        $(function () {
            // 發送訊息
            $('#btnSend').click(function () {
                var myName = $('#me').val();
                var toUserName = $('#toUserName').val();
                var msg = $('#msgContext').val();
                $.ajax({
                    type: 'post',
                    url: 'serverPush1.ashx',
                    data: { action: 'send', me: myName, toUserName: toUserName, msg: msg },
                    success: function (data) {
                        if (data.Status == 'ok') {
                            $('#ulMsg').append($('<li>我對' + toUserName + '說:' + msg + '</li>'));
                            $('#msgContext').val('');
                        } else {
                            alert('發送出錯');
                        }
                    },
                    error: function () { alert('發送出錯'); }
                });
            });

            // 登入開始接收
            $('#btnLogin').click(function () {
                rev();
                $(this).attr("disabled", "disabled");
            });
        });
    </script>
</head>
<body>
    發送人:<input type="text" id="me" /><input type="button" id="btnLogin" value="登陸" /><br />
    接收人:<input type="text" id="toUserName" /><br />
    輸入訊息:<input type="text" id="msgContext" /><input type="button" id="btnSend" value="發送" /><br />
    聊天記錄:<br />
    <ul id="ulMsg"></ul>
</body>
</html>

關鍵技術細節與優化點

環節策略目的
輪詢間隔Thread.Sleep(500)平衡即時性與資料庫負載
訊息處理讀取後立即刪除避免重複推送、資料堆積
前端重連成功/失敗皆呼叫 rev()維持長連接存活、網路異常自動恢復
連接管理登入按鈕啟用後禁用防止重複建立多條長連接

生產環境擴展建議

  • SignalR:現代 ASP.NET 應用首選,自動處理連接管理、重連、擴展性
  • WebSocket:真雙向通訊,需 IIS 8.0+ 支援
  • Redis Pub/Sub:取代資料庫輪詢,大幅降低延遲與資料庫壓力
  • 負載平衡:長連接需 Sticky Session 或外部訊息匯流排

此 ServerPush 實作展示了在無 WebSocket/SignalR 環境下,利用長輪詢模擬伺服器推送的經典模式,理解其原理有助於把握即時通訊技術演進脈絡。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved