基于.net6的WPF程序使用SignalR进行通信

之前写的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<ChatHub>("/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<string, string> dicUsers = new Dictionary<string, string>();
        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);
        }
        /// <summary>
        /// 向所有客户端发送消息
        /// </summary>
        /// <param name="user"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task SendPublicMessage(string user, string message)
        {                                                     //string user,
            await Clients.All.SendAsync("ReceivePublicMessage", user, message);   //ReceiveMessage 提供给客户端使用
        }

        /// <summary>
        /// 用户登录,密码就不判断了
        /// </summary>
        /// <param name="userId"></param>
        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.建立界面

界面代码

<Window x:Class="SignalRClient.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-compatibility/2006"
        xmlns:local="clr-namespace:SignalRClient"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
                <TextBlock>账号:</TextBlock>
                <TextBox Name="user" Width="300" Height="20" Margin="0,5"></TextBox>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock>密码:</TextBlock>
                <TextBox Name="password" Width="300" Height="20" Margin="0,5"></TextBox>
            </StackPanel>
            <StackPanel Orientation="Horizontal"  >
                <Button Name="btnLogin" Width="50" Height="20" Margin="0,5" Click="btnLogin_Click">登录</Button>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock>发送给某人:</TextBlock>
                <TextBox Name="toUser" Width="300" Height="20" Margin="0,5" ></TextBox>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock>发送内容:</TextBlock>
                <TextBox Name="content" Width="300" Height="20" Margin="0,5"></TextBox>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Button Name="btnSendAll" Width="100" Height="20" Margin="0,5" Click="btnSendAll_Click">发送所有人</Button>
                <Button Name="btnSendOne" Width="100" Height="20" Margin="0,5" Click="btnSendOne_Click">发送到个人</Button>
            </StackPanel>
            <RichTextBox Height="100" Name="rtbtxt">
                <FlowDocument>
                    <Paragraph>
                        <Run Text=""/>
                    </Paragraph>
                </FlowDocument>
            </RichTextBox>
        </StackPanel>
    </Grid>
</Window>

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
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    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();


        }
        /// <summary>
        /// 初始化
        /// </summary>
        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<string> LoginUser;
        string msgContent;

        /// <summary>
        /// 监听数据的变化
        /// </summary>
        private void Listen()
        {
            hubConnection.On<List<string>>("dicUsers", msg =>
            {
                LoginUser = msg;
                string s = string.Empty;
                foreach (string item in msg)
                {
                    s += item + "用户登录" + Environment.NewLine;
                }
                rtbtxt.AppendText(s);


            });  //匿名方法  真实环境中,此处使用的是属性变化,不要使用赋值的方式
            hubConnection.On<string>("ReceivePublicMessageLogin", msg => { msgContent = msg; rtbtxt.AppendText(msg + Environment.NewLine); });
            hubConnection.On<string, string>("ReceivePublicMessage", (user, msg) => { msgContent = msg; rtbtxt.AppendText(user + "说:" + msg + Environment.NewLine); });  //匿名方法
            hubConnection.On<string>("ChatInfo", msg => { msgContent = msg; rtbtxt.AppendText(msg + Environment.NewLine); });
        }

        /// <summary>
        /// 连接
        /// </summary>
        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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/40627.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于html2canvas和jspdf将document DOM节点转换为图片生成PDF文件,并下载到本地

这里要用到html2canvas将document DOM节点转换为图片&#xff0c;并下载到本地_你挚爱的强哥的博客-CSDN博客前端用原生js编辑文件内容→创建生成文件(格式可以自定义)→下载文件_你挚爱的强哥的博客-CSDN博客。会自动创建一个html文件。https://blog.csdn.net/qq_37860634/art…

stb_image简单使用

简介stb_image stb_image 是一个非常轻量级的、单文件的图像加载库&#xff0c;用于加载和解码多种图像格式&#xff08;如BMP、JPEG、PNG、GIF等&#xff09;的图像数据。它由Sean T. Barrett开发&#xff0c;并以公共领域&#xff08;Public Domain&#xff09;许可发布&…

字符函数和字符串函数上篇(详解)

❤️ 作者简介 &#xff1a;RO-BERRY 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识&#xff0c;对纯音乐有独特的喜爱 &#x1f4d7; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;如果你也感兴趣的话欢迎关注博主&#xff0c;期待更新 字符函数和字符串函数 &a…

详解GPT技术发展脉络

文章目录 前言关于本篇的分享内容大语言模型大模型语言模型 百花齐放TransformerAuto-RegressiveResnetLayer-NormMaskScaled Dot-Product AttentionMulti-Head AttenionSelf-AttentionPositional Encoding关于并行计算关于长程依赖Transformer演化 GPT SeriesGPT-1GPT-2GPT-3 …

unity 2019 内置渲染管线 光照与Lighting面板 参数详解

文章目录 前言一 Unity的光照 与 烘焙光照1 unity完整的光照组成2 光的亮度与颜色3 全局光照直接光间接光5 间接光≠光照贴图 二 色彩空间与自动烘焙1 unity的色彩空间2 自动烘焙光照 三 烘焙1 什么是烘焙&#xff0c;烘焙的是什么2 如何进行烘焙3 烘焙的优点和缺点4 查看光照贴…

相交链表——力扣160

题目描述 法一&#xff09;哈希表 class Solution{ public:ListNode* getIntersectionNode (ListNode* headA, ListNode* headB){unordered_set<ListNode*> st;ListNode* temp headA;while(temp){st.insert(temp);temp temp->next;}temp headB;while(temp){if(st.c…

python+allure+jenkins

目录 前言 在 python 中使用 allure 1. 安装 pytest 2. 安装 pytest-allure-adaptor 3. 使用 pytest 执行测试用例并生成 allure 中间报告&#xff08;此步骤可以省略&#xff0c;因为在 jenkins job 中会配置执行类似的命令&#xff09; 4. Jenkins 中安装Allure Jenkin…

《生活教育》期刊简介及投稿邮箱

《生活教育》期刊简介及投稿邮箱 《生活教育》杂志创办于1934&#xff0c;是中华人民共和国教育部主管的国家重点学术期刊&#xff0c;国家级期刊&#xff0c;中国知网全文收录G4期刊&#xff0c;它的理论是陶行知教育思想的主线和重要基石&#xff0c;陶行知的教育理论&#…

【C#】并行编程实战:使用延迟初始化提高性能

在前面的章节中讨论了 C# 中线程安全并发集合&#xff0c;有助于提高代码性能、降低同步开销。本章将讨论更多有助于提高性能的概念&#xff0c;包括使用自定义实现的内置构造。 毕竟&#xff0c;对于多线程编程来讲&#xff0c;最核心的需求就是为了性能。 延迟初始化 - .NET…

C#安装.Net平台科学计算库Math.Net Numerics

工作的时候需要使用到C#的Math.Net库来进行计算。 Math.Net库涵盖的主题包括特殊函数&#xff0c;线性代数&#xff0c;概率模型&#xff0c;随机数&#xff0c;插值&#xff0c;积分&#xff0c;回归&#xff0c;优化问题等。 这里记录一下&#xff0c;安装Math.Net库的过程…

el-date-picker组件的picker-options常规属性设置

查询已发生的配置项 // 日期选择器快捷键配置&#xff08;一般过去时&#xff09; pickerOptions: {shortcuts: [{text: 今天,onClick(picker) {let start new Date();let end new Date();picker.$emit(pick, [start, end]);}},{text: 昨天,onClick(picker) {let start new…

uniapp微信小程序使用axios(vue3+axios+ts版)

版本号 "vue": "^3.2.45", "axios": "^1.4.0", "axios-miniprogram-adapter": "^0.3.5", 安装axios及axios适配器&#xff0c;适配小程序 yarn add axios axios-miniprogram-adapter 使用axios 在utils创建utils/…

Flask SQLAlchemy_Serializer ORM模型序列化

在前后端分离项目中&#xff0c;经常需要把ORM模型转化为字典&#xff0c;再将字典转化为JSON格式的字符串。在遇到sqlalchemy_serializer之前&#xff0c;我都是通过类似Java中的反射原理&#xff0c;获取当前ORM模型的所有字段&#xff0c;然后写一个to_dict方法来将字段以及…

计算机vcruntime140.dll丢失的解决方法,重新安装教程

vcruntime140.dll是Microsoft Visual C Redistributable文件中的一个动态链接库&#xff08;DLL&#xff09;。这个文件是由Microsoft开发的&#xff0c;用于支持C编程语言的运行环境。vcruntime140.dll是Windows系统非常重要的文件&#xff0c;通常会被一些应用程序或游戏所需…

Jenkins | 获取凭证密码

目录 方法一&#xff1a;查看所有账号及密码 方法二&#xff1a;查看指定账号密码 方法一&#xff1a;查看所有账号及密码 Jenkins > 系统管理 > 脚本命令行 com.cloudbees.plugins.credentials.SystemCredentialsProvider.getInstance().getCredentials().forEach{i…

Linux进程

Linux进程 对于进程的理解&#xff0c;我们要从计算机的重要的冯诺依曼体系结构讲起&#xff0c;只有知道我们的程序/文件是如何在计算机中被操作运行并输出到显示器中&#xff0c;通过对于操作系统的理解&#xff0c;才能对于进程进行一定的理解。 文章目录 Linux进程冯诺依…

【Linux】udp服务器实现大型网络聊天室

udp终结篇~ 文章目录 前言一、udp服务器实现大型网络聊天室总结 前言 根据上一篇文章中对于英汉互译和远程操作的两个小功能大家应该已经学会了&#xff0c;我们的目的是让大家可以深刻的理解udp服务器所需要的接口已经实现的简单步骤&#xff0c;下面我们开始实现网络聊天室。…

3.SpringBoot 返回Html界面

1.添加依赖spring-boot-starter-web <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>2.创建Html界面 在Resources/static 文件夹下面建立对应的html&#xff0c…

快速排序的非递归实现、归并排序的递归和非递归实现、基数排序、排序算法的时间复杂度

文章目录 快速排序的非递归三数取中法选取key快速排序三路划分 归并排序的递归归并排序的非递归计数排序稳定性排序算法的时间复杂度 快速排序的非递归 我们使用一个栈来模拟函数的递归过程&#xff0c;这里就是在利用栈分区间。把一个区间分为 [left,keyi-1][key][keyi1,right…

用百度地图api获取当前定位,获取经纬度——前端笔记

问题&#xff1a; 做一个按钮&#xff0c;点击后可以获取到当前位置的经纬度&#xff0c;并渲染地图。 效果如下: 代码如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head><title>获取当前定位测试<…
最新文章