.net6API使用SignalR+vue3聊天+WPF聊天

news2024/11/25 1:00:22

目录

一、.net6api接口

二、vue3前端

三、WPF客户端


此案例分为3部分。首先创建.net6api接口,然后使用前端vue3进行聊天,再使用wpf客户端进行聊天,并且互通聊天。

一、.net6api接口

1.首先建立一个能正常运行的api,然后增加ChatHub

2.ChatHub.cs代码

其中注释已经写了,目前的功能是全部发送消息和1对1发送消息,也可以建立组,进行组发送消息。

官网参考

使用 ASP.NET Core SignalR 中的中心 | Microsoft Learn

ASP.NET Core SignalR JavaScript 客户端 | Microsoft Learn

using Microsoft.AspNetCore.SignalR;
using System.Security.Cryptography;

namespace SignalRApi.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
            {
                //如果当前用户不在线,正常是保存数据库,等上线时加载,暂时不做处理
            }
        }

    }
}

3.Program.cs代码

一定要写指定的ip地址,否则报错

Cannot send data if the connection is not in the 'Connected' State 

  

using SignalRApi.Controllers;

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();

4.效果

控制器不取消的话,如图所示

地址后面增加字符串,弹框如下,那么api就算是完成了 

补充说明:如果调试的时候可以使用,实际用的时候,发布Nginx,或者IIS,其中iis要下载安装运行时。

二、vue3前端

1.首先使用HBuilder X建立一个可以运行的vue3程序

然后安装下面@microsoft/signalr,不需要安装@aspnet/signalr,因为前者更加新,后者已经淘汰了。

npm init -y
npm install @microsoft/signalr

2.代码

ui 界面随便做一下,主要看效果就行了

<template>

	<el-input v-model="userid" :disabled="isdisabled" placeholder="输入账号" />
	<el-input v-model="password" :disabled="isdisabled" placeholder="输入密码" />
	<el-button type="primary" :disabled="isdisabled" @click="Login">登录</el-button>
	<div>
		<ul>
			<li v-for="user in LoginUser">{{user}}用户登录</li>
		</ul>
	</div>
	<el-input v-model="sendUserid" :disabled="!isdisabled" placeholder="发送给某人" />
	<el-input v-model="sendContent" :disabled="!isdisabled" placeholder="发送内容" />
	<el-button type="primary" :disabled="!isdisabled" @click="SendAll">发送所有人</el-button>
	<el-button type="primary" :disabled="!isdisabled" @click="SendUser">发送到个人</el-button>
	<div>
		<ul>
			<li v-for="user in msgContent">{{user}}</li>
		</ul>
	</div>
	<!-- <input type="text" v-model="state.userMessage" v-on:keypress="txtMsgOnkeypress" />
	<div>
		<ul>
			<li v-for="(msg,index) in state.messages" :key="index">{{msg}}</li>
		</ul>
	</div>
	<el-button type="primary">发送</el-button>
	<el-button type="success">Success</el-button>
	<el-button type="info">Info</el-button>
	<el-button type="warning">Warning</el-button>
	<el-button type="danger">Danger</el-button> -->
</template>

<script setup>
	import {
		ref
	} from 'vue'
	import {
		reactive,
		onMounted
	} from 'vue'
	import * as signalR from '@microsoft/signalr'

	defineProps({
		msg: String
	})

	var connection
	const LoginUser = ref([])

	const msgContent = reactive([])

	const isdisabled = ref(false)
	const userid = ref('')
	const password = ref('')
	const sendUserid = ref('')
	const sendContent = ref('')

	const Login = async () => {
		connection = new signalR.HubConnectionBuilder()
			.withUrl(' http://127.0.0.1:5196/api/chat') //这里一定要写指定的ip,否则报错,大坑搞了1天的时间
			.withAutomaticReconnect().build();
		await connection.start();

		connection.on('dicUsers', msg => { //监听用户登录的信息,前后端要一致
			console.log(msg)
			LoginUser.value = msg
			console.log(LoginUser.value)
		});

		connection.on('ReceivePublicMessageLogin', (Loginmsg) => { //监听登录的信息,前后端要一致
			console.log(Loginmsg)
			msgContent.push(Loginmsg)
			console.log(msgContent.value)
			console.log('aaa')
		});
		connection.on('ReceivePublicMessage', (user, msg) => { //监听发送的信息,前后端要一致
			console.log(user)
			console.log(msg)
			msgContent.push(user + "说:" + msg)
			console.log(msgContent.value)
			console.log('bbb')
		});

		connection.on('ChatInfo', msg => { //监听单独发送的信息,前后端要一致
			console.log("单独说:" + msg)
			msgContent.push("单独说:" + msg)
			console.log("单独说:" + msgContent.value)
			console.log('ccc')
		});

		await connection.invoke("Login", userid.value); //发送消息
		// connection.on('ReceivePublicMessage', msg => {
		// 	state.messages.push(msg); //监听发送的信息,前后端要一致
		// });
		isdisabled.value = true
		console.log('wewew')
	}
	const SendAll = async () => {
		console.log(userid.value)
		console.log(sendContent.value)
		await connection.invoke("SendPublicMessage", userid.value, sendContent.value); //发送消息
		console.log('zxzx')
	}

	const SendUser = async () => {
		console.log(sendContent.value)
		await connection.invoke("ChatOne", userid.value, sendUserid.value, sendContent.value); //发送消息
		console.log('55fff')
	}

	// onMounted(async function() {
	// 	connection = new signalR.HubConnectionBuilder()
	// 		.withUrl(' http://127.0.0.1:5196/api/chat') //这里一定要写指定的ip,否则报错,大坑搞了1天的时间
	// 		.withAutomaticReconnect().build();
	// 	await connection.start();
	// 	connection.on('ReceivePublicMessage', msg => {
	// 		state.messages.push(msg);
	// 	});
	// });
</script>

<style scoped>
	a {
		color: #42b983;
	}
</style>

3.运行效果

此时把api运行起来,然后运行vue3项目。

可以1对所有人说,也可以1对1说

三、WPF客户端

1.首先建立一个wpf程序,其实winform也可以

为了简单,wpf就不使用mvvm的方式了,只为实现效果

2.安装Microsoft.AspNetCore.SignalR.Client

3.建立界面

为了方便简单,界面按照vue3的界面做

界面代码 

<Window x:Class="SignalRWPF.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:SignalRWPF"
        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>

后台代码,此处代码可以看vue3怎么调用的就行了,照猫画虎即可,这只是普通的写法,还可以使用属性变化,加上MVVM。

using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Collections.Generic;
using System.Linq;
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.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SignalRWPF
{
    /// <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:5196/api/chat").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 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);
        }
    }
}

4.效果

同时打开api,打开2个窗体,然后进行对话。 

至此,所有代码就完成了,那么我们把api,vue,wpf都运行起来,然后进行聊天演示。

源码:

https://gitee.com/602874946/signal-rs 

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

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

相关文章

redis未授权访问漏洞的三种场景复现以及加固思路

1.redis简介 redis是一个 非常快速 的&#xff0c;开源的&#xff0c;支持网络&#xff0c;可以基于内存&#xff0c;也可以持久化的日志型&#xff0c; 非关系型 的键值对数据库。并提供了多种语言的api。有java&#xff0c;c/c,c#,php&#xff0c;JavaScript&#xff0c;per…

1.Linux编程-gcc编译器

gcc的工作流程 gcc编译器将c源文件到生成一个可执行程序,中间一共经历了四个步骤: 四个步骤并不是gcc独立完成的,而是在内部调用了其他工具,从而完成了整个工作流程, 其中编译最耗时, 因为要逐行检查语法. gcc的工作流程: 1 预处理: cpp预处理器, 去掉注释, 展开头文件, …

Java进程CPU高负载排查步骤

近期发现服务器Java进程负载&#xff0c;超过100%一、采用top命令定位进程登录服务器&#xff0c;执行top命令&#xff0c;查看CPU占用情况&#xff0c;找到进程的pid很容易发现&#xff0c;PID为29706的java进程的CPU飙升到700%多&#xff0c;且一直降不下来&#xff0c;很显然…

python—requests模块详解

一、前言 1、requests简介 requests是一个很实用的Python HTTP客户端库&#xff0c;爬虫和测试服务器响应数据时经常会用到&#xff0c;它是python语言的第三方的库&#xff0c;专门用于发送HTTP请求&#xff0c;使用起来比urllib更简洁也更强大。 2、requests库的安装 方法…

CUDA中的数学方法

CUDA中的数学方法 文章目录CUDA中的数学方法1. Standard FunctionsSingle-Precision Floating-Point FunctionsDouble-Precision Floating-Point Functions2. Intrinsic FunctionsSingle-Precision Floating-Point FunctionsDouble-Precision Floating-Point Functions参考手册…

ROPR:一款功能强大的极速多线程ROPGadget查找工具

关于ROPR ROPR是一款速度极快且功能强大的ROPGadget查找工具&#xff0c;该工具支持多线程运行&#xff0c;可以帮助广大研究人员快速寻找和定位目标ROPGadget。 ROP&#xff08;Return Oriented Programming&#xff09;&#xff0c;即返回导向编程&#xff0c;而ROPGadget是…

算法训练营DAY52|1143.最长公共子序列、1035.不相交的线、53. 最大子序和

前两道题思路是一模一样的&#xff0c;但是需要认真理解&#xff0c;最后一道虽然思路不算难&#xff0c;但是需要注意的细节一点不少。 1143. 最长公共子序列 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/longest-common-subsequence/最长公共子序列…

2-ChatGPT插件到Google浏览器、Firefox浏览器,并进行ChatGPT工作。

ChatGPT插件到Google浏览器、Firefox浏览器&#xff0c;并进行ChatGPT工作。1&#xff0c;首先&#xff0c;在装插件ChatGPT forGoogle插件时&#xff0c;我没安装成功&#xff0c;问题是我没有区分Google浏览器和双核浏览器的差别。2&#xff0c;如何使用ChatGPT在浏览器上进行…

基于北方苍鹰算法优化LSTM(NGO-LSTM)研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

2023软件测试面试全方位话术,你想要的全都有

目录 2023软件测试面试话术包含&#xff1a;自我介绍简历人资题话术面试题项目介绍 自我介绍 软件测试基础面试题 人资高频率问题 简历 总结 2023软件测试面试话术包含&#xff1a;自我介绍简历人资题话术面试题项目介绍 目录如下 自我介绍 软件测试基础面试题 需求的标准…

参数嗅探 - 为何产生

这个问题会在参数话的SQL语句&#xff08;例如存储过程&#xff09;与SQL Server里的计划缓存机制结合的时候会出现。这个文章分为2个部分&#xff0c;第1部分会介绍下参数嗅探&#xff08;Parameter Sniffing&#xff09;的概况&#xff0c;第2部分我们介绍下如何解决这个问题…

git整体工作流程

文章目录本地流程远程仓库流程git 代码量统计本地流程 工作区 暂存区 本地仓库 远程仓库流程 git 代码量统计 # 统计本地仓库的代码量 git log --all --since2022-01-01 --until2022-01-05 --format"%aN" --no-merges | sort | uniq -c | while read data; do na…

浅析 SeaweedFS 与 JuiceFS 架构异同

SeaweedFS 是一款高效的分布式文件存储系统&#xff0c;最早的设计原型参考了 Facebook 的 Haystack&#xff0c;具有快速读写小数据块的能力。本文将通过对比 SeaweedFS 与 JuiceFS 在设计与功能上的差异&#xff0c;以帮助读者进行更适合自己的选择。 SeaweedFS 系统结构 S…

【树】平衡二叉搜索树的介绍与构建

二叉平衡搜索树一、平衡二叉搜索树的概述1. 平衡二叉树的性质2. 平衡二叉树的最小节点数&#xff08;公式及其原理&#xff09;a. 树高度和深度的区别b. 原理二、平衡二叉树的创建和调整1. 节点2. 旋转四种姿态a. LL旋转b. RR旋转c. LR旋转d. RL旋转2. 节点的插入3. 节点的删除…

[转]深度学习 Transformer架构解析

原文链接&#xff1a;https://blog.csdn.net/mengxianglong123/article/details/1262614791.1 Transformer的诞生2018年10月&#xff0c;Google发出一篇论文《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》, BERT模型横空出世, 并横扫N…

分享5款后台收到的推荐最多的软件

最近后台收到好多小伙伴的私信&#xff0c;今天继续推荐五款小工具&#xff0c;都是免费使用的&#xff0c;大家可以去试试看。 1.视频压缩——Moo0 VideoMinimizer 一款完全免费的视频压缩软件&#xff0c;能够有效的将视频压缩到最小&#xff0c;同时还不改变视频画质很清晰…

基于SpringBoot的房屋租赁管理系统的设计与实现

基于SpringBoot的房屋租赁管理系统的设计与实现 1 绪论 1.1 课题来源 随着社会的不断发展以及大家生活水平的提高&#xff0c;越来越多的年轻人选择在大城市发展。在大城市发展就意味着要在外面有一处安身的地方。在租房的过程中&#xff0c;大家也面临着各种各样的问题&…

Java程序设计-基于Java高校社团管理系统

摘 要功能需求系统的功能实现摘 要 当前&#xff0c;大多数高校的社团信息管理都是采用纸质档案的方式来管理的&#xff0c;这样不仅不能长期的保存信息&#xff0c;而且在数据的查找上带来很大的不方便。在目前的网络技术和计算机技术的普及和信息管理的迅速发展&#xff0c;…

【MySQL】你知道的MySQL中的集合函数有哪些呢?

集合函数排名AVG&#xff08;&#xff09;函数COUNT()函数SUM&#xff08;&#xff09;函数MAX&#xff08;&#xff09;函数 和 MIN&#xff08;&#xff09;函数总结大家好&#xff0c;我是小冷。 上一篇写了 看看ChatGPT是如何回答面试官的问题的&#xff1f; 地址是&#x…

MySQL数据库06——条件查询(WHERE)

MySQL条件查询&#xff0c;主要是对数据库里面的数据按照一定条件进行筛选&#xff0c;主要依靠的是WHERE语句进行。 先来了解一下基础的条件运算。 关系运算符 逻辑运算符 逻辑运算符优先级&#xff1a;NOT>AND>OR&#xff0c;关系运算符>逻辑运算符 SQL特殊运算符…