之前写的SignalR通信,是基于.net6api,BS和CS进行通信的。
.net6API使用SignalR+vue3聊天+WPF聊天_signalr wpf_故里2130的博客-CSDN博客
今天写一篇关于CS客户端的SignalR通信,后台服务使用.net6api 。其实和之前写的差不多,主要在于服务端以后台进程的方式存在,而客户端以exe方式存在,其实代码都一样,只是生成的方式不一样。
一、服务端
1.首先建立一个.net6的webapi服务端
2.Program.cs
using SignalRServerApi.Controllers;
namespace SignalRServerApi
{
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSignalR(); //增加AddSignalR
string[] urls = new[] { "http://localhost:3000" }; //此处一定要写指定的ip地址,地址是前端的ip地址,坑了我1天的时间
builder.Services.AddCors(options =>
options.AddDefaultPolicy(builder => builder.WithOrigins(urls)
.AllowAnyMethod().AllowAnyHeader().AllowCredentials())
);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors(); //增加跨域问题
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.MapHub("/api/chat"); //前端访问的地址,2边要统一就行了
app.Run();
}
}
}
3.ChatHub.cs
using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;
namespace SignalRServerApi.Controllers
{
public class ChatHub : Hub
{
private static Dictionary dicUsers = new Dictionary();
public override Task OnConnectedAsync() //登录
{
Console.WriteLine($"ID:{Context.ConnectionId} 已连接"); //控制台记录
var cid = Context.ConnectionId;
//根据id获取指定客户端
var client = Clients.Client(cid);
//向指定用户发送消息
//client.SendAsync("Self", cid);
//像所有用户发送消息
Clients.All.SendAsync("ReceivePublicMessageLogin", $"{cid}加入了聊天室"); //界面显示登录
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception? exception) //退出的时候
{
Console.WriteLine($"ID:{Context.ConnectionId} 已断开");
var cid = Context.ConnectionId;
//根据id获取指定客户端
var client = Clients.Client(cid);
//向指定用户发送消息
//client.SendAsync("Self", cid);
//像所有用户发送消息
Clients.All.SendAsync("ReceivePublicMessageLogin", $"{cid}离开了聊天室"); //界面显示登录
return base.OnDisconnectedAsync(exception);
}
///
/// 向所有客户端发送消息
///
///
///
///
public async Task SendPublicMessage(string user, string message)
{ //string user,
await Clients.All.SendAsync("ReceivePublicMessage", user, message); //ReceiveMessage 提供给客户端使用
}
///
/// 用户登录,密码就不判断了
///
///
public void Login(string userId) //对应前端的invoke
{
if (!dicUsers.ContainsKey(userId))
{
dicUsers[userId] = Context.ConnectionId;
}
Console.WriteLine($"{userId}登录成功,ConnectionId={Context.ConnectionId}");
//向所有用户发送当前在线的用户列表
Clients.All.SendAsync("dicUsers", dicUsers.Keys.ToList()); //对应前端的on
}
public void ChatOne(string userId, string toUserId, string msg) //用户 发送到的用户 发送的消息
{
string newMsg = $"{userId}对你说{msg}";//组装后的消息体
//如果当前用户在线
if (dicUsers.ContainsKey(toUserId))
{
Clients.Client(dicUsers[toUserId]).SendAsync("ChatInfo", newMsg);
}
else
{
//如果当前用户不在线,正常是保存数据库,等上线时加载,暂时不做处理
}
}
}
}
4.生成方式
选择Windows应用程序
5.运行
运行后,服务是以进程的方式存在
6.效果
此时需要注意代码的这个地址
当然IP和端口都可以修改的,也可以增加网页显示,根据业务而定。
二、客户端
1.首先建立一个.net6的wpf客户端
2.安装Microsoft.AspNetCore.SignalR.Client
3.建立界面
界面代码
账号:
密码:
发送给某人:
发送内容:
4.后台代码
using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
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 SignalRClient
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
private HubConnection hubConnection;
public MainWindow()
{
InitializeComponent();
//rtbtxt.AppendText("4444");
}
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
//此处和VUE3界面是一样的,参照写就行了。
//1.初始化
InitInfo();
//2.连接
Link();
//3.监听
Listen();
//4.登录
Login();
}
///
/// 初始化
///
private void InitInfo()
{
hubConnection = new HubConnectionBuilder().WithUrl("http://127.0.0.1:5000/api/chat", (opt) =>
{
opt.HttpMessageHandlerFactory = (message) =>
{
if (message is HttpClientHandler clientHandler)
// bypass SSL certificate
clientHandler.ServerCertificateCustomValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => { return true; };
return message;
};
}).WithAutomaticReconnect().Build();
hubConnection.KeepAliveInterval = TimeSpan.FromSeconds(5);
}
List LoginUser;
string msgContent;
///
/// 监听数据的变化
///
private void Listen()
{
hubConnection.On>("dicUsers", msg =>
{
LoginUser = msg;
string s = string.Empty;
foreach (string item in msg)
{
s += item + "用户登录" + Environment.NewLine;
}
rtbtxt.AppendText(s);
}); //匿名方法 真实环境中,此处使用的是属性变化,不要使用赋值的方式
hubConnection.On("ReceivePublicMessageLogin", msg => { msgContent = msg; rtbtxt.AppendText(msg + Environment.NewLine); });
hubConnection.On("ReceivePublicMessage", (user, msg) => { msgContent = msg; rtbtxt.AppendText(user + "说:" + msg + Environment.NewLine); }); //匿名方法
hubConnection.On("ChatInfo", msg => { msgContent = msg; rtbtxt.AppendText(msg + Environment.NewLine); });
}
///
/// 连接
///
private async void Link()
{
try
{
await hubConnection.StartAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private static bool ValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
// 在这里添加你的证书验证逻辑
// 返回true表示验证通过,返回false表示验证失败
// 例如,你可以添加自定义的证书验证逻辑来允许不受信任的证书
return true;
}
private void Login()
{
hubConnection.InvokeAsync("Login", user.Text);
}
private void btnSendAll_Click(object sender, RoutedEventArgs e)
{
hubConnection.InvokeAsync("SendPublicMessage", user.Text, content.Text);
}
private void btnSendOne_Click(object sender, RoutedEventArgs e)
{
hubConnection.InvokeAsync("ChatOne", user.Text, toUser.Text, content.Text);
}
}
}
这里需要注意, 一起运行不会报错,但是单独运行会报错
The SSL connection could not be established, see inner exception
需要在初始化InitInfo()方法中增加HttpMessageHandlerFactory,即可解决。
5.效果
此时,后台的服务以进行的方式存在,然后可以和客户端进行通信,其实和之前写的是一样的,只是生成方式不同而已。
源码
https://download.csdn.net/download/u012563853/88061397