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

news2024/11/29 10:56:57

之前写的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.coloradmin.cn/o/769026.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

怎样查看Oracle的sid和service_name

用sysdba身份登录 比如 conn / as sysdba 匿名管理员登陆执行 select name form V$database; 或是执行select * from V$database;不过执行第二个显示的内容太多了不好找自己想要的结果你也可以先用desc V$database;语句查看一下V$database都有什么字段然后选择自己想要的字段…

【每天40分钟,我们一起用50天刷完 (剑指Offer)】第二十九天 29/50【数据流中的中位数( 大根堆 + 小根堆 )】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

百度卫星地图切换个性化地图

1.切换 if (mapType.value 1) {mapInstance.setMapType(BMAP_SATELLITE_MAP);//卫星地图} else if (mapType.value 2) {mapInstance.setMapType(BMAP_NORMAL_MAP);//标准地图mapInstance.setMapStyleV2({ styleJson: darkJson });//个性化样式}2.不带路况的卫星地图 mapIn…

Haystack:建立端到端的NLP应用程序的工具箱

Haystack是一个端到端的自然语言处理&#xff08;NLP&#xff09;框架&#xff0c;可以使用语言模型、Transformer模型、向量搜索等功能来构建NLP应用程序。无论您想进行问题回答、答案生成、语义文档搜索&#xff0c;还是构建能够进行复杂决策和查询解决的工具&#xff0c;都可…

php伪协议(文件包含-强行读取文件)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 php伪协议 伪协议详情 php伪协议 文件包含直接读取的是文件&#xff0c;而不是文件源码&#xff0c;所以要想办法读取源码方法。 那么就要涉及到 PHP 伪协议 ph…

Vulkan 同步

前言 在前面的文章中&#xff0c;我们讲解了Vulkan的多线程设计理念&#xff0c;分析了其底层的机制。我们知道在Vulkan的设计中&#xff0c;尽量避免资源的同步竞争&#xff0c;但是在某些复杂场景和多线程优化过程中难免会遇到资源竞争的问题&#xff0c;这时候就需要同步机…

Redis实战案例19-Redis解决主从一致性问题

主节点&#xff08;Master&#xff09;“写操作”&#xff1a; 接收并响应客户端的读写请求。持久化数据到磁盘&#xff08;根据配置可以选择使用RDB快照或者AOF日志&#xff09;。将自己的写操作同步给所有的从节点。处理发布/订阅&#xff08;Pub/Sub&#xff09;模式中的发…

shell脚本备份数据库

首先是在本地windows环境下尝试备份数据库 打开mysql的bin目录&#xff0c;然后在地址栏cmd&#xff0c;进入cmd界面&#xff0c;输入mysqldump命令&#xff0c;-u输入用户名&#xff0c;-p输入密码 还有数据库名称&#xff0c;以及后面要保存到的位置 mysqldump -uroot -p tes…

数据标准化助力生产企业产能提升

毫无疑问&#xff0c;多产线、多工厂的协同管理成为集团构建核心竞争力的关键因素。企业内部供应链、研制链的业务贯通与协作需要基于统一的数据标准、统一的集成规范开展&#xff0c;数据标准化将成为企业数字化转型的有效赋能手段。 一、客户诉求 近年来&#xff0c;随着国…

OpenAI科学家Jason Wei关于人工智能的回复

问&#xff1a;你的人工智能之路是如何开始的&#xff1f; 答&#xff1a;虽然你们知道我的工作可能是从 2022 年开始的&#xff0c;但我从 2017 年就开始从事人工智能工作了。在达特茅斯大学读本科时&#xff0c;我原本想成为华尔街的一名银行家&#xff0c;这是我父母那一代…

ORAN上行帧和下行帧的定时关系

在UE天线端口处&#xff0c;相同帧号的UL无线帧&#xff0c;相对于DL无线帧有定时提前偏移。N_TA offset是应用于UE的默认定时偏移参数&#xff0c;除非根据3GPP 36.211或38.211中规定&#xff0c;在小区中配置了另一个值。n-ta-offset是M平面参数&#xff0c;用于控制在O-RU天…

软件设置开机自启

文章目录 一、使用系统自带工具设置开机启动1.winR键打开运行窗口2.输入"shell:startup"→点击确定3.将要开机自启的软件快捷方式放到启动目录下就行 一、使用系统自带工具设置开机启动 1.winR键打开运行窗口 2.输入"shell:startup"→点击确定 3.将要开机…

FastEdit ⚡:在10秒内编辑大型语言模型

概述&#xff1a; 这个仓库旨在通过一个单一的命令&#xff0c;有效地将新鲜且定制化的知识注入到大型语言模型中&#xff0c;以辅助开发人员的工作。 支持的模型&#xff1a;○ GPT-J (6B)○ LLaMA (7B/13B)○ BLOOM (7.1B)○ Falcon (7B)○ Baichuan (7B/13B)○ InternLM (7…

【es】部署后打不开访问页面

具体报错&#xff1a; [2023-07-18T00:55:28,203][WARN ][o.e.x.s.t.n.SecurityNetty4HttpServerTransport] [demo] received plaintext http traffic on an https channel, closing connection Netty4HttpChannel{localAddress/127.0.0.1:9200, remoteAddress/127.0.0.1:5529…

企业应如何面对数字化时代?

企业要想有效面对数字时代&#xff0c;就需要适应和拥抱数字技术带来的机遇和挑战。以下是企业需要考虑的一些关键策略&#xff1a; 拥抱数字化转型&#xff1a;数字化转型涉及重新思考和重新构想业务流程、产品和服务以利用数字技术。这可能包括自动化流程、采用云计算、实施数…

如何学习Java集合框架? - 易智编译EaseEditing

要学习Java集合框架相关的技术和知识&#xff0c;可以按照以下步骤进行&#xff1a; 掌握Java基础知识&#xff1a; 在学习集合框架之前&#xff0c;确保你已经具备良好的Java编程基础&#xff0c;包括语法、面向对象编程&#xff08;OOP&#xff09;原理和常用的核心类库等。…

HIVE SQL几种不同的分组排序函数比较

--数据建表 drop table if exists db.tb_name; create table if not exists db.tb_name ( name string,class string,score string ) ; insert overwrite table db.tb_name values ("a","1","100") ,("b","1","99"…

pdf怎么转换成TXT格式?分享这3个方法!

当我们需要从PDF文件中提取文本时&#xff0c;尤其是在收集资料撰写文章或其他文件时&#xff0c;PDF文件的特性可能会造成一些麻烦。PDF文件的版面内容不会受浏览设备的影响&#xff0c;保持着统一的版式&#xff0c;但同时也不易于编辑和修改。因此&#xff0c;常见的解决方案…

基于SSM+Vue的汉服文化平台网站设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

CleanMyMac X2023中文版升级下载及功能讲解

CleanMyMac X是一款功能强大的Mac系统优化和清理工具。它提供了一系列功能来帮助您优化和保护您的Mac电脑。coco玛奇朵要来分享的是Mac下一款强大、实用的系统清理软件——CleanMyMac&#xff0c;它可以帮助您快速清理系统垃圾&#xff0c;清理Photos重复照片&#xff0c;卸载软…