开源 C# .net mvc 开发(九)websocket--服务器与客户端的实时通信

发布于:2025-09-02 ⋅ 阅读:(21) ⋅ 点赞:(0)

 文章的目的为了记录.net mvc学习的经历。本职为嵌入式软件开发,公司安排开发文件系统,临时进行学习开发,系统上线3年未出没有大问题。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。 

相关链接:

开源 C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客

开源 C# .net mvc 开发(三)WEB内外网访问-CSDN博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示-CSDN博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客

开源 C# .net mvc 开发(六)发送邮件、定时以及CMD编程-CSDN博客

开源 C# .net mvc 开发(七)动态图片、动态表格和json数据生成-CSDN博客

开源 C# .net mvc 开发(八)IIS Express轻量化Web服务器的配置和使用-CSDN博客

 推荐链接:

开源 java android app 开发(一)开发环境的搭建-CSDN博客

开源 java android app 开发(二)工程文件结构-CSDN博客

开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客

开源 java android app 开发(四)GUI界面重要组件-CSDN博客

开源 java android app 开发(五)文件和数据库存储-CSDN博客

开源 java android app 开发(六)多媒体使用-CSDN博客

开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客

开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客

开源 java android app 开发(九)后台之线程和服务-CSDN博客

开源 java android app 开发(十)广播机制-CSDN博客

开源 java android app 开发(十一)调试、发布-CSDN博客

开源 java android app 开发(十二)封库.aar-CSDN博客

开源 java android app 开发(十三)绘图定义控件、摇杆控件的制作-CSDN博客

主要内容:一个基于ASP.NET MVC和WebSocket的实时通信应用。

1.  应用场景

2.  核心源码分析

3.  所有源码

4.  效果演示

一、应用场景

1.  实时通信系统‌。
‌在线聊天应用‌:支持即时消息收发(如微信、WhatsApp)、消息状态提示(如“对方正在输入”)、已读回执等交互功能。‌‌
多人在线游戏‌:实时同步玩家操作(如角色移动、攻击动作),确保游戏体验的流畅性和公平性。‌‌

2‌.  实时数据推送‌。
‌金融交易平台‌:毫秒级推送股票价格、外汇汇率等市场数据,帮助投资者快速决策。‌‌

3‌.  体育赛事直播‌:动态更新比分、球员状态及比赛事件,用户无需刷新页面即可获取最新进展。‌‌
‌协同办公与远程交互‌。
‌文档协作工具‌:如 Google Docs,允许多用户同时编辑并实时同步内容。‌‌
‌视频会议系统‌:保持低延迟的语音和视频流传输,提升远程会议质量。‌‌

4‌.  物联网与监控系统‌。
‌智能设备控制‌:实时双向交互指令,如智能家居设备的远程操控。‌‌
‌工业监控‌:传输传感器数据(如温度、压力)并即时响应异常警报。‌‌

 

二、核心源码分析

1. WebSocket服务器 (MvcApplication类)
Global.asax文件中

public static WebSocketServer WsServer;
public static readonly ConcurrentDictionary<string, WebSocketSession> UserSessions;


关键特性:

使用SuperWebSocket库实现WebSocket服务器

监听所有网络接口(0.0.0.0:2019)

使用线程安全的ConcurrentDictionary管理用户会话

支持用户认证和消息广播


2. 会话管理机制
Global.asax文件中

// 新连接处理
UserSessions[userName] = session;

// 消息发送
public static bool SendToUser(string userName, string message)

// 连接清理
UserSessions.TryRemove(userEntry.Key, out _);


3. 前端通信逻辑
WebSocket连接:

index.cshtml文件中

ws = new WebSocket('ws://192.168.3.126:2019?user=' + encodeURIComponent(userName));

三.  所有源码

1.  index.cshtml


@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data", id = "uploadForm" }))
{
    @Html.AntiForgeryToken()

    <div class="form-group">
        <label for="file">登录名:</label>
        <input type="text" name="name" id="name" class="form-control" />
    </div>

    <div class="form-group">
        <label for="file">密码:</label>
        <input type="text" name="pwd" id="pwd" class="form-control" />
    </div>

 


}
<div class="form-group">

    <input type="text" id="ajaxmsg" placeholder="ajax消息" />
    <button onclick="sendAjax()">ajax发送</button>
</div>


<br />
<br />
<br />
<br />
<div class="form-group">
    <button onclick="myCon()">连接</button>
</div>
<div class="form-group">

    <input type="text" id="websocketmsg" placeholder="websocket消息" />
    <button onclick="sendWebSocket()">websocket发送</button>
</div>


<div id="output"></div>
<script type="text/javascript">

    var ws;
    appendToOutput("123");
    // 表单提交事件处理


    function myCon() {

        var userName = document.getElementById('name').value;
        var userPwd = document.getElementById('pwd').value;


        if (!userName || !userPwd) {

            alert('请填写所有必填字段');
            return false;
        }




        // 建立WebSocket连接(携带用户名参数)
        connectWebSocket(userName);

        return true;

    }



    function connectWebSocket(userName) {
        // 关闭现有连接
        if (ws) {
            ws.close();
        }

        // 连接到 WebSocket 服务器,携带用户名参数
        //ws = new WebSocket('ws://127.0.0.1:2019?user=' + encodeURIComponent(userName));
        ws = new WebSocket('ws://192.168.3.126:2019?user=' + encodeURIComponent(userName));

        ws.onopen = function () {
            console.log('WebSocket connection opened for user:', userName);

        };

        ws.onmessage = function (evt) {
            try {
                var data = JSON.parse(evt.data);
                console.log('收到消息:', data);

                // 处理服务器发送的消息
                appendToOutput('服务器: ' + data.message);

                switch (data.type) {
                    case 'processing_start':
                    case 'processing':

                        break;
                    case 'tts_complete':
                        appendToOutput(text);
                        break;
                    case 'welcome':
                        // 忽略欢迎消息,或者可以显示
                        break;
                }
            } catch (e) {
                appendToOutput('收到: ' + evt.data);
            }
        };

        ws.onclose = function (event) {
            console.log('Connection closed.', event.code, event.reason);
            appendToOutput('WebSocket 连接已关闭');
        };

        ws.onerror = function (error) {
            console.error('WebSocket Error: ', error);
            appendToOutput('WebSocket 连接错误');
        };


        var msg = document.getElementById("websocketmsg").value;

    }


    // 新增:发送简单字符串(非JSON格式)
    function sendWebSocket(message) {
        message = document.getElementById("websocketmsg").value;

        if (!ws || ws.readyState !== WebSocket.OPEN) {
            console.error('WebSocket连接未就绪');
            return false;
        }

        try {
            // 直接发送字符串(如果服务器支持简单字符串协议)
            ws.send(message);
            appendToOutput('发送: ' + message);
            return true;
        } catch (error) {
            console.error('发送字符串失败:', error);
            return false;
        }
    }

    function appendToOutput(text) {
        var output = document.getElementById('output');
        output.innerHTML += '<p>' + text + '</p>';
    }


    function sendAjax(strCustom) {
        //alert(1);
        var userName = document.getElementById('name').value;
        var userPwd = document.getElementById('pwd').value;
        strCustom = document.getElementById("ajaxmsg").value;

        if (!userName || !userPwd || !strCustom) {

            alert('请填写所有必填字段');
            return false;
        }



        $.ajax({
            url: "/Home/sendAjax?name=" + userName + "&pwd=" + userPwd + "&str=" + strCustom,
            type: "get",
            data: {},
            success: function (res) {
                //alert(res);

            }, dataType: "text"
        });
    }

</script>


2.  Global.asax文件

using Newtonsoft.Json;
using SuperWebSocket;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

/*

IP 绑定问题:Setup("127.0.0.1", port) 只监听本地回环地址。这意味着只有服务器本机上的浏览器或客户端才能建立 WebSocket 连接。
其他设备无法连接。通常需要改为 Setup("0.0.0.0", port) 来监听所有网络接口。

防火墙和端口开放:端口 2019 需要在服务器防火墙中开放,外部请求才能访问。

扩展性限制:

单服务器:所有连接都保存在内存字典中,这意味着它无法扩展到多台服务器(无状态扩展)。如果使用负载均衡,连接会分散到不同服务器,
一台服务器上的客户端无法收到另一台服务器上客户端的广播消息。

解决方案:对于需要水平扩展的场景,通常需要引入 Redis Pub/Sub 或 Azure SignalR 等背板服务来在服务器实例之间转发消息。

 */


namespace WebApplication1
{
    public class MvcApplication : System.Web.HttpApplication
    {

        public static WebSocketServer WsServer;

        // 按用户名存储WebSocket会话
        public static readonly ConcurrentDictionary<string, WebSocketSession> UserSessions
            = new ConcurrentDictionary<string, WebSocketSession>();

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            StartWebSocketServer();
        }

        private void StartWebSocketServer()
        {
            WsServer = new WebSocketServer();
            int port = 2019;

            WsServer.NewSessionConnected += WsServer_NewSessionConnected;
            WsServer.NewMessageReceived += WsServer_NewMessageReceived;
            WsServer.SessionClosed += WsServer_SessionClosed;

            //if (!WsServer.Setup("127.0.0.1", port))
            //if (!WsServer.Setup("0.0.0.0", port))
            if (!WsServer.Setup("0.0.0.0", port))
            {
                System.Diagnostics.Debug.WriteLine("WebSocket服务器设置失败");
                return;
            }

            if (!WsServer.Start())
            {
                System.Diagnostics.Debug.WriteLine("WebSocket服务器启动失败");
                return;
            }

            System.Diagnostics.Debug.WriteLine($"WebSocket服务器已启动,端口: {port}");
        }

        // 新会话连接事件处理
        private static void WsServer_NewSessionConnected(WebSocketSession session)
        {
            try
            {
                /*
                // 从查询参数获取用户名
                var queryParams = HttpUtility.ParseQueryString(session.Path);
                string userName = queryParams["user"] ?? "anonymous";
                */

                string userName = "anonymous";

                // 直接使用 Uri 类来解析
                var uri = new Uri("ws://0.0.0.0" + session.Path); // 添加虚拟协议和域名
                var queryParams = HttpUtility.ParseQueryString(uri.Query);

                userName = queryParams["user"] ?? "anonymous";


                // 移除该用户之前的会话(避免重复连接)
                if (UserSessions.TryRemove(userName, out WebSocketSession oldSession))
                {
                    try
                    {
                        oldSession.Close();
                    }
                    catch { }
                }

                // 添加新会话
                UserSessions[userName] = session;

                System.Diagnostics.Debug.WriteLine($"用户 {userName} 已连接,会话ID: {session.SessionID}");

                // 发送欢迎消息(只发给当前用户)
                session.Send(JsonConvert.SerializeObject(new
                {
                    type = "welcome",
                    message = $"欢迎 {userName},连接已建立",
                    user = userName,
                    timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
                }));
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"新会话连接错误: {ex.Message}");
            }
        }

        // 接收到新消息事件处理
        private static void WsServer_NewMessageReceived(WebSocketSession session, string msg)
        {
            try
            {
                // 这里可以处理客户端发来的消息,如果需要的话
                System.Diagnostics.Debug.WriteLine($"收到消息: {msg}");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"处理消息错误: {ex.Message}");
            }
        }

        // 会话关闭事件处理
        private static void WsServer_SessionClosed(WebSocketSession session, SuperSocket.SocketBase.CloseReason reason)
        {
            try
            {
                // 找到并移除该会话
                var userEntry = UserSessions.FirstOrDefault(x => x.Value.SessionID == session.SessionID);
                if (!string.IsNullOrEmpty(userEntry.Key))
                {
                    UserSessions.TryRemove(userEntry.Key, out _);
                    System.Diagnostics.Debug.WriteLine($"用户 {userEntry.Key} 已断开连接");
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"会话关闭错误: {ex.Message}");
            }
        }

        // 新增:向特定用户发送消息
        public static bool SendToUser(string userName, string message)
        {
            try
            {
                if (UserSessions.TryGetValue(userName, out WebSocketSession session))
                {
                    if (session.Connected)
                    {
                        session.Send(message);
                        System.Diagnostics.Debug.WriteLine($"消息已发送给用户: {userName}");
                        return true;
                    }
                    else
                    {
                        // 会话已断开,清理
                        UserSessions.TryRemove(userName, out _);
                        return false;
                    }
                }
                System.Diagnostics.Debug.WriteLine($"用户 {userName} 未找到或未连接");
                return false;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"发送给用户 {userName} 的消息失败: {ex.Message}");
                return false;
            }
        }

        protected void Application_End()
        {
            WsServer?.Stop();
        }
    }
}

3.  HomeControl.cs文件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;


using System.IO;
using Newtonsoft.Json; //https://www.nuget.org/packages/Newtonsoft.Json
using RestSharp;
using System.Security.Cryptography;
using WebApplication1.Models;

namespace WebApplication1.Controllers
{
    public class HomeController : Controller
    {
        private Model1 db = new Model1();



        string name = "";

        public ActionResult Index()
        {

            /*
            string strAsr = myASR();
            string strLim =  myLIM(strAsr);
            myTTS(strLim); 
            */
            return View();
        }










        // 精确发送消息给特定用户
        private bool SendWebSocketMessage(string userName, object message)
        {
            try
            {
                var jsonMessage = JsonConvert.SerializeObject(message);
                return MvcApplication.SendToUser(userName, jsonMessage);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"发送WebSocket消息给 {userName} 失败: {ex.Message}");
                return false;
            }
        }


        public ActionResult SendMessageToAllClients(string message)
        {
            // 检查 WebSocket 服务器是否已启动且有客户端连接
            if (MvcApplication.WsServer != null && MvcApplication.WsServer.SessionCount > 0)
            {
                // 向所有客户端发送消息
                foreach (var session in MvcApplication.WsServer.GetAllSessions())
                {
                    session.Send($"Server says: {message}");
                }
                return Content($"Message '{message}' sent to all connected clients.");
            }
            return Content("No WebSocket clients connected or server not running.");

        }

    


        public string sendAjax(string name,string pwd,string str)
        {


            // 用户认证
            int n = db.myuser.Where(R => R.name == name && R.pwd == pwd).Count();
            if (n == 0)
            {
                return ""; // 认证失败直接返回,不发送WebSocket消息
            }
            if (!string.IsNullOrEmpty(str))
            {
                SendWebSocketMessage(name, str);
            }
            return str;
        }

  

    }
}

4.  myuser的sql文本,用于创建sqlserver数据库

CREATE TABLE [dbo].[myuser] (
    [Id]      INT            IDENTITY (1, 1) NOT NULL,
    [name]    NVARCHAR (MAX) NULL,
    [pwd]     NVARCHAR (MAX) NULL,
    [type]    NVARCHAR (MAX) NULL,
    [company] NVARCHAR (MAX) NULL,
    [right]   NVARCHAR (MAX) NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);

四.  效果演示

1.写入用户名和密码,连接则建立websocket连接。输入websocket信息,页面显示发送的消息。

2.输入ajax信息,点击发送,IIS Express服务器上显示,收到的字符串。



网站公告

今日签到

点亮在社区的每一天
去签到