文章的目的为了记录.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服务器上显示,收到的字符串。