C# 中 WebSocket 与 SignalR:实时通信的两种选择
在现代 Web 应用中,实时通信变得越来越重要。无论是聊天应用、在线游戏、股票行情推送还是协作编辑工具,都需要服务器能够主动向客户端推送数据。在 .NET 生态系统中,WebSocket 和 SignalR 是实现这一功能的两个主要方案。
本文将对这两种技术进行比较,分析它们的异同点和使用场景,并提供简单示例代码帮助你快速上手。
一、什么是 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:实时通信的两种选择的更多相关文章
- Java 获取*.properties配置文件中的内容 ,常见的两种方法
import java.io.InputStream; import java.util.Enumeration; import java.util.List; import java.util.Pr ...
- 在Java Web程序中使用监听器可以通过以下两种方法
之前学习了很多涉及servlet的内容,本小结我们说一下监听器,说起监听器,编过桌面程序和手机App的都不陌生,常见的套路都是拖一个控件,然后给它绑定一个监听器,即可以对该对象的事件进行监听以便发生响 ...
- Eclipse中SVN的安装步骤(两种)和使用方法
Eclipse中SVN的安装步骤(两种)和使用方法 一.给Eclipse安装SVN,最常见的有两种方式:手动方式和使用安装向导方式.具体步骤如下: 方式一:手动安装 1.下载最新的Eclipse,我的 ...
- [转]MFC子线程中更新控件内容的两种办法
一.概述 每个系统中都有线程(至少都有一个主线程),而线程最重要的作用就是并行处理,提高软件的并发率.针对界面来说,还能提高界面的响应能力.一般的,为了应用的稳定性,在数据处理等耗时操作会单独在一个线 ...
- python中字典的循环遍历的两种方式
开发中经常会用到对于字典.列表等数据的循环遍历,但是python中对于字典的遍历对于很多初学者来讲非常陌生,今天就来讲一下python中字典的循环遍历的两种方式. 注意: python2和python ...
- js如何实现动态的在表格中添加和删除行?(两种方法)
js如何实现动态的在表格中添加和删除行?(两种方法) 一.总结 1.table元素有属性和一些方法(js使用) 方法一:添加可通过在table的innerHTML属性中添加tr和td来实现 tab.i ...
- QT中获取选中的radioButton的两种方法(动态取得控件的objectName之后,对名字进行比较)
QT中获取选中的radioButton的两种方法 QT中要获取radioButton组中被选中的那个按钮,可以采用两种如下两种办法进行: 方法一:采用对象名称进行获取 代码: 1 QRadioBu ...
- Android中H5和Native交互的两种方式
Android中H5和Native交互的两种方式:http://www.jianshu.com/p/bcb5d8582d92 注意事项: 1.android给h5页面注入一个对象(WZApp),这个对 ...
- C语言中存储多个字符串的两种方式
C语言中存储多个字符串的两种方式 方式一 二维字符串数组 声明: char name[][] = { "Justinian", "Momo", " ...
- Swift基础之两种选择星星的评价样式并获取星星的索引值
想练练手,所以封装了一个两种选择星星的评价样式的Demo,并且可以获取到点击的星星的索引值,方便记录值,上传数据时使用 首先创建View类,设计初始化方法,并且用到了枚举类型和代理方法 方式一:默认的 ...
随机推荐
- flutter-iOS数字键盘无法属于小数点
keyboardType:TextInputType.numberWithOptions(decimal: true),
- Linux环境Docker使用代理推拉镜像
闲扯几句 不知不觉已经2月中了,1个半月忙得没写博客,这篇其实很早就想写了(可追溯到Docker刚刚无法拉镜像的时候),由于工作和生活上的事比较多又在备考软考架构,拖了好久-- 简单记录下怎么做的,以 ...
- Java基础常见知识、面试题总结 (上)
Java语言有哪些特点 简单易学: 面向对象(封装,继承,多态): 平台无关性( Java 虚拟机实现平台无关性): 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来 ...
- TTRSS 迁移实战
背景 几年前在云服务器上搭建了一套 TTRss + RssHub 集群, 用于阅读文章. 具体可以参见这篇文章: 使用 K8S 部署 RSS 全套自托管解决方案 - RssHub + Tiny Tin ...
- 【目标检测】一、初始的R-CNN与SVM
1.流程 为什么要用SVM而不是CNN最后一层的softmax? 取什么模型必然是有标准衡量,这个流程取得是书上[4]写的,作者说他得实验证明SVM比FC的mAP要高,所以我流程暂且这样画了. R-C ...
- Linux云服务器如何安装配置Redis
为大家分享linux云服务器如何安装配置Redis,示例为CentOS7系统 Redis 是一款开源的高性能内存数据库,以极速读写(微秒级)和丰富数据结构著称,支持字符串.哈希.列表等类型,兼具持久化 ...
- excel 日期列显示到日
原来显示 效果图: 步骤
- 四大AI编程工具组合测评
在当今数字化浪潮中,AI 编程工具如雨后春笋般涌现,极大地提升了编程效率与体验.本文将详细剖析四类 AI 编程工具组合,从开发工具.大模型.插件搭配,到编程能力.费用体系及综合评价,为开发者提供全面 ...
- 【Vue3】下载zip文件损坏的问题
需求: 需要在vue3上实现Asp.net Web API 下载zip包的功能,本身需求很简单,但是中间遇到了问题,记录一下. 问题: 下载的zip包和后端的zip包大小不一致,后端生成的zip 61 ...
- 多维度实测DeepSeek新模型DeepSeek-V3-0324,编程能力超强!
大家好,我是六哥!今天必须给大伙唠唠DeepSeek全新v3.就在昨晚,DeepSeek悄没声儿地在Huggingface上发布了DeepSeek-V3-0324.虽说不是全新模型,可能力提升那叫一个 ...