文章目录
- 前言
- 环境
- Webapi开发测试
-
- 服务端开发
- 客户端开发
-
- 官方文档
- 新建项目
- 添加代码
-
- MainWindow.xaml
- MainWindow.xaml.cs
- 运行结果
- 代码解析
-
- 服务端
- 客户端
- 总结
前言
我之前稍微研究了一下SignalR Core。用起来还行。简单来说SignalR就是用来解决实时通讯的问题的。
ASP.NET Core SingleR:初次体验和简单项目搭建
SignalR支持三种客户端,C#,Java,JavaScirpt。基本够用了。本身就是微软开发的,肯定支持自己的语言。因为是Websocket的上层封装,所以也要支持Websocket的主要客户,JavaScirpt。不过我没想到有Java版本的,那这样双语言互通的问题也就解决了。
环境
- .net core 8.0
- visual studio 2022
Webapi开发测试
服务端开发
我们新建一个WebApi
按照我之前设置的
.NET Core webapi 从零开始在IIS上面发布后端接口
再添加一下上次添加的聊天室代码
ASP.NET Core SingleR:初次体验和简单项目搭建
Program.cs
var builder = WebApplication.CreateBuilder(args);
//配置跨域
var MyPolicy = "MyPolicy";
builder.Services.AddCors(options =>
{
options.AddPolicy(MyPolicy, policy =>
{
policy.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod();
});
});
//启用SignalR
builder.Services.AddSignalR();
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
//添加swagger接口配置
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "API标题",
Description = $"API描述,v1版本"
});
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
//IncludeXmlComments 第二参数 true 则显示 控制器 注释
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename), true);
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
}
app.UseSwagger();
app.UseSwaggerUI();
//自动重定向到swgger文件
app.UseStatusCodePagesWithRedirects("/swagger/index.html");
app.UseCors(MyPolicy);
app.UseHttpsRedirection();
app.UseAuthorization();
//SignalR位置
app.MapHubChatHub>("/ChatHub");
app.MapControllers();
app.Run();
ChatHub
using Microsoft.AspNetCore.SignalR;
namespace SiganlRTest.Hubs
{
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
客户端开发
官方文档
ASP.NET Core SignalR .NET 客户端
为了方便操作的实时性,我们新建一个WPF程序
微软官方示例代码
新建项目
Nuget添加
添加代码
MainWindow.xaml
Window x:Class="SignalR_WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-comp服务器托管网atibility/2006"
xmlns:local="clr-namespace:SignalR_WPF"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
Grid Margin="0,0,193.333,50.667">
Grid.ColumnDefinitions>
ColumnDefinition Width="109*" />
ColumnDefinition Width="288*" />
Grid.ColumnDefinitions>
Button x:Name="connectButton"
Content="Connect"
HorizontalAlignment="Left"
Margin="275.333,63,0,0"
VerticalAlignment="Top"
Width="95"
Click="connectButton_Click"
Height="41"
Grid.Column="1" />
Button x:Name="sendButton"
Content="Send Message"
HorizontalAlignment="Left"
Margin="275.333,113,0,0"
VerticalAlignment="Top"
Width="95"
Click="sendButton_Click"
Height="41"
Grid.Column="1"
IsEnabled="False" />
TextBox x:Name="messageTextBox"
HorizontalAlignment="Left"
Height="41"
Margin="82,113,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="311"
Grid.ColumnSpan="2" />
ListBox x:Name="messagesList"
HorizontalAlignment="Left"
Height="141"
Margin="82,170,0,0"
VerticalAlignment="Top"
Width="311"
RenderTransformOrigin="-0.304,0.109"
BorderThickness="1"
Grid.ColumnSpan="2"
BorderBrush="Gainsboro" />
TextBox x:Name="userTextBox"
HorizontalAlignment="Left"
Height="41"
Margin="82,57,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="311"
Grid.ColumnSpan="2" />
Label Content="User"
HorizontalAlignment="Left"
Height="31"
Margin="39,63,0,0"
VerticalAlignment="Top"
Width="38" />
Label Content="Message"
HorizontalAlignment="Left"
Height="26"
Margin="19,120,0,0"
VerticalAlignment="Top"
Width="58" />
Grid>
Window>
MainWindow.xaml.cs
using Microsoft.AspNetCore.SignalR.Client;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace SignalR_WPF
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
HubConnection connection;
public MainWindow()
{
InitializeComponent();
connection = new HubConnectionBuilder()
.WithUrl("http://localhost:53353/ChatHub")
.Build();
#region snippet_ClosedRestart
connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await connection.StartAsync();
};
#endregion
}
private async void connectButton_Click(object sender, RoutedEventArgs e)
{
#region snippet_ConnectionOn
connection.Onstring, string>("ReceiveMessage", (user, message) =>
{
this.Dispatcher.Invoke(() =>
{
var newMessage = $"{user}: {message}";
messagesList.Items.Add(newMessage);
});
});
#endregion
try
{
await connection.StartAsync();
messagesList.Items.Add("Connection started");
connectButton.IsEnabled = false;
sendButton.IsEnabled = true;
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}
}
private async void sendButton_Click(object sender, RoutedEventArgs e)
{
#region snippet_ErrorHandling
try
{
#region snippet_InvokeAsync
await connection.InvokeAsync("SendMessage",
userTextBox.Text, messageTextBox.Text);
#endregion
}
catch (Exception ex)
{
messagesList.Items.Add(ex.Message);
}
#endregion
}
}
}
运行结果
代码解析
SignalR的代码将服务器的输入和输出分为两类:
- 输入:类似于Http的接口
- 输出:类似于Http的回调,因为SignalR的数据是通关另一个口进行返回的
#mermaid-svg-5OziOmi3lMDgpCDd {font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-5OziOmi3lMDgpCDd .error-icon{fill:#552222;}#mermaid-svg-5OziOmi3lMDgpCDd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5OziOmi3lMDgpCDd .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-5OziOmi3lMDgpCDd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5OziOmi3lMDgpCDd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5OziOmi3lMDgpCDd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5OziOmi3lMDgpCDd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5OziOmi3lMDgpCDd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5OziOmi3lMDgpCDd .marker.cross{stroke:#333333;}#mermaid-svg-5OziOmi3lMDgpCDd svg{font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5OziOmi3lMDgpCDd .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-5OziOmi3lMDgpCDd text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-5OziOmi3lMDgpCDd .actor-line{stroke:grey;}#mermaid-svg-5OziOmi3lMDgpCDd .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-5OziOmi3lMDgpCDd .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-5OziOmi3lMDgpCDd #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-5OziOmi3lMDgpCDd .sequenceNumber{fill:white;}#mermaid-svg-5OziOmi3lMDgpCDd #sequencenumber{fill:#333;}#mermaid-svg-5OziOmi3lMDgpCDd #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-5OziOmi3lMDgpCDd .messageText{fill:#333;stroke:#333;}#mermaid-svg-5OziOmi3lMDgpCDd .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-5OziOmi3lMDgpCDd .labelText,#mermaid-svg-5OziOmi3lMDgpCDd .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-5OziOmi3lMDgpCDd .loopText,#mermaid-svg-5OziOmi3lMDgpCDd .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-5OziOmi3lMDgpCDd .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-5OziOmi3lMDgpCDd .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-5OziOmi3lMDgpCDd .noteText,#mermaid-svg-5OziOmi3lMDgpCDd .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-5OziOmi3lMDgpCDd .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-5OziOmi3lMDgpCDd .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-5OziOmi3lMDgpCDd .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-5OziOmi3lMDgpCDd .actorPopupMenu{position:absolute;}#mermaid-svg-5OziOmi3lMDgpCDd .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-5OziOmi3lMDgpCDd .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-5OziOmi3lMDgpCDd .actor-man circle,#mermaid-svg-5OziOmi3lMDgpCDd line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-5OziOmi3lMDgpCDd :root{–mermaid-font-family:”trebuchet ms”,verdana,arial,sans-serif;}
服务端
客户端
接口1
接口2
接口3
回调1
回调2
回调3
服务端
客户端
服务端
#mermaid-svg-6aJb3eucRd6KbCxy {font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6aJb3eucRd6KbCxy .error-icon{fill:#552222;}#mermaid-svg-6aJb3eucRd6KbCxy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6aJb3eucRd6KbCxy .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-6aJb3eucRd6KbCxy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6aJb3eucRd6KbCxy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6aJb3eucRd6KbCxy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6aJb3eucRd6KbCxy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6aJb3eucRd6KbCxy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6aJb3eucRd6KbCxy .marker.cross{stroke:#333333;}#mermaid-svg-6aJb3eucRd6KbCxy svg{font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6aJb3eucRd6KbCxy .label{font-family:”trebuchet ms”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-6aJb3eucRd6KbCxy .cluster-label text{fill:#333;}#mermaid-svg-6aJb3eucRd6KbCxy .cluster-label span{color:#333;}#mermaid-svg-6aJb3eucRd6KbCxy .label text,#mermaid-svg-6aJb3eucRd6KbCxy span{fill:#333;color:#333;}#mermaid-svg-6aJb3eucRd6KbCxy .node rect,#mermaid-svg-6aJb3eucRd6KbCxy .node circle,#mermaid-svg-6aJb3eucRd6KbCxy .node ellipse,#mermaid-svg-6aJb3eucRd6KbCxy .node polygon,#mermaid-svg-6aJb3eucRd6KbCxy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6aJb3eucRd6KbCxy .node .label{text-align:center;}#mermaid-svg-6aJb3eucRd6KbCxy .node.clickable{cursor:pointer;}#mermaid-svg-6aJb3eucRd6KbCxy .arrowheadPath{fill:#333333;}#mermaid-svg-6aJb3eucRd6KbCxy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6aJb3eucRd6KbCxy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6aJb3eucRd6KbCxy .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-6aJb3eucRd6KbCxy .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-6aJb3eucRd6KbCxy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6aJb3eucRd6KbCxy .cluster text{fill:#333;}#mermaid-svg-6aJb3eucRd6KbCxy .cluster span{color:#333;}#mermaid-svg-6aJb3eucRd6KbCxy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6aJb3eucRd6KbCxy :root{–mermaid-font-family:”trebuchet ms”,verdana,arial,sans-serif;}
客户端
#mermaid-svg-54IPNApnIDaeBEea {font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-54IPNApnIDaeBEea .error-icon{fill:#552222;}#mermaid-svg-54IPNApnIDaeBEea .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-54IPNApnIDaeBEea .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-54IPNApnIDaeBEea .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-54IPNApnIDaeBEea .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-54IPNApnIDaeBEea .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-54IPNApnIDaeBEea .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-54IPNApnIDaeBEea .marker{fill:#333333;stroke:#333333;}#mermaid-svg-54IPNApnIDaeBEea .marker.cross{stroke:#333333;}#mermaid-svg-54IPNApnIDaeBEea svg{font-family:”trebuchet ms”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-54IPNApnIDaeBEea .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-54IPNApnIDaeBEea text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-54IPNApnIDaeBEea .actor-line{stroke:grey;}#mermaid-svg-54IPNApnIDaeBEea .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-54IPNApnIDaeBEea .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-54IPNApnIDaeBEea #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-54IPNApnIDaeBEea .sequenceNumber{fill:white;}#mermaid-svg-54IPNApnIDaeBEea #sequencenumber{fill:#333;}#mermaid-svg-54IPNApnIDaeBEea #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-54IPNApnIDaeBEea .messageText{fill:#333;stroke:#333;}#mermaid-svg-54IPNApnIDaeBEea .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-54IPNApnIDaeBEea .labelText,#mermaid-svg-54IPNApnIDaeBEea .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-54IPNApnIDaeBEea .loopText,#mermaid-svg-54IPNApnIDaeBEea .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-54IPNApnIDaeBEea .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-54IPNApnIDaeBEea .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-54IPNApnIDaeBEea .noteText,#mermaid-svg-54IPNApnIDaeBEea .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-54IPNApnIDaeBEea .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-54IPNApnIDaeBEea .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-54IPNApnIDaeBEea .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-54IPNApnIDaeBEea .actorPopupMenu{position:absolute;}#mermaid-svg-54IPNApnIDaeBEea .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-54IPNApnIDaeBEea .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-54IPNApnIDaeBEea .actor-man circle,#mermaid-svg-54IPNApnIDaeBEea line{stroke:hsl(259.626168服务器托管网2243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-54IPNApnIDaeBEea :root{–mermaid-font-family:”trebuchet ms”,verdana,arial,sans-serif;}
客户端
服务端
监听回调1
监听回调2
监听回调…
尝试连接+断线自动重连
接口发送数据
返回回调,触发之前监听的接口
客户端
服务端
所以我们一般的代码逻辑
- 监听所有的回调
- 尝试连接
- 连接成功,发送接口
- 服务器返回回调,回调到监听的接口
总结
我们目前是单接口,单回调。收发数据,那还是比较简单。那么如果我们想对SignalR的接口进行复杂的调用呢?下篇文章我会对这个进行思考和探索。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net