在现代 Web 应用中,实时通信变得越来越重要。无论是聊天应用、在线游戏、股票行情推送还是协作编辑工具,都需要服务器能够主动向客户端推送数据。在 .NET 生态系统中,WebSocketSignalR 是实现这一功能的两个主要方案。

本文将对这两种技术进行比较,分析它们的异同点和使用场景,并提供简单示例代码帮助你快速上手。


一、什么是 WebSocket?

WebSocket 是 HTML5 提供的一种全双工通信协议,允许客户端与服务器之间建立持久连接,实现双向实时通信。相比传统的 HTTP 请求-响应模式,WebSocket 更加高效,适合需要频繁交互的应用。

WebSocket 特点:

  • 基于 TCP 协议
  • 支持双向通信
  • 连接是长连接(保持打开)
  • 轻量级,性能高
  • 需要手动管理连接和消息处理

使用场景:

  • 实时数据推送(如股票行情)
  • 在线多人游戏
  • 多人协作编辑
  • IoT 设备通信等

二、什么是 SignalR?

SignalR 是微软开发的一个基于 .NET 的库,它简化了实时通信的实现。SignalR 内部封装了多种传输方式(包括 WebSocket、Server-Sent Events、长轮询等),并根据浏览器和网络环境自动选择最优的方式。

SignalR 特点:

  • 抽象了底层通信细节
  • 支持跨平台(.NET Core / .NET 6+)
  • 自动降级兼容性差的浏览器
  • 提供 Hub 模式,易于组织业务逻辑
  • 可集成到 ASP.NET Core 中

使用场景:

  • 快速构建实时功能(无需关注底层通信细节)
  • Web 应用中的通知系统
  • 即时通讯(IM)应用
  • 实时仪表盘、状态监控

三、WebSocket vs SignalR 对比

特性 WebSocket SignalR
通信方式 全双工 全双工(通过封装)
是否需要手动处理连接 需要 不需要
是否支持多路复用 不支持 支持(Hub)
是否支持自动降级 不支持 支持(长轮询等)
开发复杂度 较高 较低
性能 略低于 WebSocket
适合场景 高性能、定制化通信 快速开发、通用实时功能

四、示例代码

示例 1:WebSocket 服务端 + 客户端(.NET)

服务端(控制台程序)

using System;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks; class Program
{
static async Task Main(string[] args)
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://localhost:5000/");
listener.Start();
Console.WriteLine("WebSocket Server started on http://localhost:5000"); while (true)
{
HttpListenerContext context = await listener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
WebSocketContext webSocketContext = await context.AcceptWebSocketAsync(null);
_ = HandleWebSocketConnection(webSocketContext.WebSocket);
}
else
{
context.Response.StatusCode = 400;
context.Response.Close();
}
}
} private static async Task HandleWebSocketConnection(WebSocket socket)
{
byte[] buffer = new byte[1024 * 4];
while (socket.State == WebSocketState.Open)
{
WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Received: {message}"); // Echo back the message
byte[] response = Encoding.UTF8.GetBytes($"Echo: {message}");
await socket.SendAsync(new ArraySegment<byte>(response), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}

客户端(JavaScript)

<!DOCTYPE html>
<html>
<head>
<title>WebSocket Client</title>
</head>
<body>
<input type="text" id="messageInput" placeholder="Enter message" />
<button onclick="sendMessage()">Send</button>
<div id="output"></div> <script>
const ws = new WebSocket('ws://localhost:5000');
ws.onmessage = function (event) {
document.getElementById('output').innerText += event.data + '\n';
}; function sendMessage() {
const input = document.getElementById('messageInput');
const message = input.value;
ws.send(message);
input.value = '';
}
</script>
</body>
</html>

示例 2:SignalR 服务端 + 客户端(ASP.NET Core)

服务端(Startup.cs 或 Program.cs)

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddSignalR(); var app = builder.Build(); app.MapHub<ChatHub>("/chatHub"); app.Run(); public class ChatHub : Hub
{
public async Task Send(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}

客户端(JavaScript)

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/5.0.13/signalr.min.js"></script>
<input type="text" id="userInput" placeholder="User" />
<input type="text" id="messageInput" placeholder="Message" />
<button onclick="sendMessage()">Send</button>
<div id="chat"></div> <script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/chatHub")
.build(); connection.on("ReceiveMessage", function (user, message) {
const msg = `${user}: ${message}`;
document.getElementById("chat").innerHTML += `<p>${msg}</p>`;
}); connection.start().catch(function (err) {
return console.error(err.toString());
}); function sendMessage() {
const user = document.getElementById("userInput").value;
const message = document.getElementById("messageInput").value;
connection.invoke("Send", user, message).catch(function (err) {
return console.error(err.toString());
});
document.getElementById("messageInput").value = "";
}
</script>

五、总结

场景 推荐技术
高性能、低延迟、自定义协议 WebSocket
快速开发、通用实时功能 SignalR
浏览器兼容性要求高 SignalR
需要精细控制通信过程 WebSocket

如果你正在开发一个简单的聊天室或通知系统,SignalR 是更好的选择;而如果你需要构建一个高性能、低延迟的物联网通信系统,WebSocket 更合适


六、延伸阅读


希望这篇博客对你有所帮助!如果你有更多关于 C# 或实时通信的问题,欢迎留言交流

C# 中 WebSocket 与 SignalR:实时通信的两种选择的更多相关文章

  1. Java 获取*.properties配置文件中的内容 ,常见的两种方法

    import java.io.InputStream; import java.util.Enumeration; import java.util.List; import java.util.Pr ...

  2. 在Java Web程序中使用监听器可以通过以下两种方法

    之前学习了很多涉及servlet的内容,本小结我们说一下监听器,说起监听器,编过桌面程序和手机App的都不陌生,常见的套路都是拖一个控件,然后给它绑定一个监听器,即可以对该对象的事件进行监听以便发生响 ...

  3. Eclipse中SVN的安装步骤(两种)和使用方法

    Eclipse中SVN的安装步骤(两种)和使用方法 一.给Eclipse安装SVN,最常见的有两种方式:手动方式和使用安装向导方式.具体步骤如下: 方式一:手动安装 1.下载最新的Eclipse,我的 ...

  4. [转]MFC子线程中更新控件内容的两种办法

    一.概述 每个系统中都有线程(至少都有一个主线程),而线程最重要的作用就是并行处理,提高软件的并发率.针对界面来说,还能提高界面的响应能力.一般的,为了应用的稳定性,在数据处理等耗时操作会单独在一个线 ...

  5. python中字典的循环遍历的两种方式

    开发中经常会用到对于字典.列表等数据的循环遍历,但是python中对于字典的遍历对于很多初学者来讲非常陌生,今天就来讲一下python中字典的循环遍历的两种方式. 注意: python2和python ...

  6. js如何实现动态的在表格中添加和删除行?(两种方法)

    js如何实现动态的在表格中添加和删除行?(两种方法) 一.总结 1.table元素有属性和一些方法(js使用) 方法一:添加可通过在table的innerHTML属性中添加tr和td来实现 tab.i ...

  7. QT中获取选中的radioButton的两种方法(动态取得控件的objectName之后,对名字进行比较)

    QT中获取选中的radioButton的两种方法   QT中要获取radioButton组中被选中的那个按钮,可以采用两种如下两种办法进行: 方法一:采用对象名称进行获取 代码: 1 QRadioBu ...

  8. Android中H5和Native交互的两种方式

    Android中H5和Native交互的两种方式:http://www.jianshu.com/p/bcb5d8582d92 注意事项: 1.android给h5页面注入一个对象(WZApp),这个对 ...

  9. C语言中存储多个字符串的两种方式

    C语言中存储多个字符串的两种方式 方式一    二维字符串数组 声明: char name[][] = { "Justinian", "Momo", " ...

  10. Swift基础之两种选择星星的评价样式并获取星星的索引值

    想练练手,所以封装了一个两种选择星星的评价样式的Demo,并且可以获取到点击的星星的索引值,方便记录值,上传数据时使用 首先创建View类,设计初始化方法,并且用到了枚举类型和代理方法 方式一:默认的 ...

随机推荐

  1. log4net 配置数据库连接

    http://logging.apache.org/log4net/release/config-examples.html MS SQL Server The database table defi ...

  2. c# 对序列化类XMLSerializer 二次封装泛型化方便了一些使用的步骤

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/17270107.html 加工的泛型类如下: using System; using Syste ...

  3. Thymeleaf 在页面中直接显示内容

    Thymeleaf 在页面中直接显示内容   一般情况下 Thymeleaf 模板要输出变量需要在某个标签中(如<div>.<span>)写th:text等属性来实现.但有时我 ...

  4. 微信小程序之java服务端获取openid

    微信小程序越来越热,最近团队写了一个小程序,这篇博客我将讲一下怎么通过java服务端获取到用户的openid. api文档的授权登陆地址: http://developers.weixin.qq.co ...

  5. 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!

    引子:那个让运维集体加班的夜晚 "凡哥!线上服务响应时间飙到10秒了!"凌晨1点,实习生小李的语音带着哭腔. 监控大屏上,JVM堆内存曲线像坐了火箭--刚扩容的16G内存,30分钟 ...

  6. hbase - [06] rowkey的设计

    HBase是三维有序存储的,通过rowkey(行键),column key(column family和qualifier)和TimeStamp(时间戳)这三个维度可以对HBase中的数据进行快速定位 ...

  7. Sqoop - [01] 概述

    将关系型数据库(Oracle.MySQL.PG等)数据与Hadoop数据进行转换的工具. 一.Sqoop1和Sqoop2的区别 Sqoop1由client端直接接入Hadoop,任务通过解析生成对应的 ...

  8. 【数值计算方法】数值积分&微分-python实现

    目录 数值积分 1. 引言 2. 几个常用积分公式及其复合公式 2.1 求积公式 2.2 代数精度 2.3 复合积分 2.4 常用积分公式的python实现 3. 变步长方法与外推加速技术 4. 牛顿 ...

  9. Qt 实现带阴影 无边框的QMessageBox

    Qt 实现带阴影的QMessagebox 在实际项目里面使用到了QMessageBox做一个弹窗,最开始是样式不是需要的样式,就去找了一下QMessageBox的样式表,一般来说可以使用findChi ...

  10. Linux centos 运行telnet命令command not found的解决方法

      Linux centos 运行telnet命令,出现下面的错误提示: 1 2 [root@localhost ~]# telnet 127.0.0.1 -bash: telnet: command ...