在现代 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. flutter-iOS数字键盘无法属于小数点

    keyboardType:TextInputType.numberWithOptions(decimal: true),  

  2. Linux环境Docker使用代理推拉镜像

    闲扯几句 不知不觉已经2月中了,1个半月忙得没写博客,这篇其实很早就想写了(可追溯到Docker刚刚无法拉镜像的时候),由于工作和生活上的事比较多又在备考软考架构,拖了好久-- 简单记录下怎么做的,以 ...

  3. Java基础常见知识、面试题总结 (上)

    Java语言有哪些特点 简单易学: 面向对象(封装,继承,多态): 平台无关性( Java 虚拟机实现平台无关性): 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来 ...

  4. TTRSS 迁移实战

    背景 几年前在云服务器上搭建了一套 TTRss + RssHub 集群, 用于阅读文章. 具体可以参见这篇文章: 使用 K8S 部署 RSS 全套自托管解决方案 - RssHub + Tiny Tin ...

  5. 【目标检测】一、初始的R-CNN与SVM

    1.流程 为什么要用SVM而不是CNN最后一层的softmax? 取什么模型必然是有标准衡量,这个流程取得是书上[4]写的,作者说他得实验证明SVM比FC的mAP要高,所以我流程暂且这样画了. R-C ...

  6. Linux云服务器如何安装配置Redis

    为大家分享linux云服务器如何安装配置Redis,示例为CentOS7系统 Redis 是一款开源的高性能内存数据库,以极速读写(微秒级)和丰富数据结构著称,支持字符串.哈希.列表等类型,兼具持久化 ...

  7. excel 日期列显示到日

    原来显示 效果图: 步骤

  8. 四大AI编程工具组合测评

     在当今数字化浪潮中,AI 编程工具如雨后春笋般涌现,极大地提升了编程效率与体验.本文将详细剖析四类 AI 编程工具组合,从开发工具.大模型.插件搭配,到编程能力.费用体系及综合评价,为开发者提供全面 ...

  9. 【Vue3】下载zip文件损坏的问题

    需求: 需要在vue3上实现Asp.net Web API 下载zip包的功能,本身需求很简单,但是中间遇到了问题,记录一下. 问题: 下载的zip包和后端的zip包大小不一致,后端生成的zip 61 ...

  10. 多维度实测DeepSeek新模型DeepSeek-V3-0324,编程能力超强!

    大家好,我是六哥!今天必须给大伙唠唠DeepSeek全新v3.就在昨晚,DeepSeek悄没声儿地在Huggingface上发布了DeepSeek-V3-0324.虽说不是全新模型,可能力提升那叫一个 ...