Asp.Net FrameWork 4.7.2 WebAPI 使用WebSocket协议

参考文章:Asp.net webApi 通过WebSocket推送消息给客户端,搭建一个即是服务端又是客户端的服务_c# webapi websocket-CSDN博客

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

废话不多说了,直接手把手教你如何通信。

下面是一个简单的用户连接和断开操作实例代码,有别的业务需求可以根据自己情况修改一下代码

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.WebSockets;
using WYC.DAL.DAL;namespace WYC.WebAPI.Controllers
{/// <summary>/// 聊天控制器/// </summary>[RoutePrefix("Chat")][AllowAnonymous] //不授权public class ChatController : ApiController{/// <summary>/// 记录客户端(根据用户ID一一对应)/// </summary>private static Dictionary<string, WebSocket> _listSockets = new Dictionary<string, WebSocket>();/// <summary>/// 业务逻辑层/// </summary>private readonly ChatDAL _dal = new ChatDAL();/// <summary>/// 连接/// </summary>/// <param name="userId">用户Id</param>/// <returns></returns>[Route("GetConnect"), HttpGet]public async Task<HttpResponseMessage> GetConnect(string userId){await _dal.UpdateUserOnlineState(userId, 1); //修改用户在线状态//指定执行不带参数的方法(注意:ProcessRequest(AspNetWebSocketContext context)这样属于不带参数,当前这个方法因为多了一个userId参数所以属于带参数方法)//HttpContext.Current.AcceptWebSocketRequest(ProcessRequest); //指定执行额外带参数的方法(注意:ProcessRequest(AspNetWebSocketContext context)这样属于不带参数,当前这个方法因为多了一个userId参数所以属于带参数方法)Func<AspNetWebSocketContext, Task> paramnter = async (context) =>{await ProcessRequest(context, userId);};HttpContext.Current.AcceptWebSocketRequest(paramnter);return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);}/// <summary>/// 客户端连接成功后执行方法(接收信息和发送信息)/// </summary>/// <param name="context"></param>/// <returns></returns>public async Task ProcessRequest(AspNetWebSocketContext context, string userId){var socket = context.WebSocket;_sockets.Add(socket); //存放客户端连接的WebSocket_listSockets.Add(userId, socket); //存放客户端连接的WebSocketwhile (true) //进入一个无限循环,一直监听当前连接成功的客户端操作行为{var buffer = new ArraySegment<byte>(new byte[1024]);//对web socket进行异步接收数据(相当于是等待客户端发送消息)var receivedResult = await socket.ReceiveAsync(buffer, System.Threading.CancellationToken.None);if (receivedResult.MessageType == WebSocketMessageType.Close) //客户端断开连接{await socket.CloseAsync(WebSocketCloseStatus.Empty, string.Empty, System.Threading.CancellationToken.None);//如果client发起close请求,对client进行ack_listSockets.Remove(userId); //删除break; //结束循环}if (socket.State == WebSocketState.Open) //客户端连接状态{//获取客户端发送消息内容string recvMsg = Encoding.UTF8.GetString(buffer.Array, 0, receivedResult.Count);}}}}
}

注意这里有一个小小的重点:

一、HttpContext.Current.AcceptWebSocketRequest(ProcessRequest); 
二、 Func<AspNetWebSocketContext, Task> paramnter = async (context) =>{await ProcessRequest(context, userId);};HttpContext.Current.AcceptWebSocketRequest(paramnter);

以上两个代码区别就是一个指定带参数的方法,一个指定不带参数的方法,上面的一是指定不带参数的方法:相当于指定执行ProcessRequest(AspNetWebSocketContext context)这样的方法,上面的二是指定带参数的方法:相当于指定执行ProcessRequest(AspNetWebSocketContext context, string userId)这样多了一个参数的方法。正常咱们在处理业务和数据库对接的话都会设计到带参数所以这里是个重点。

以下代码是个人成功案例,可以根据自己业务需求进行调整代码

一、控制器

注意:这里不需要下载任何什么SDK包,就是引用一下系统自带的包如下:

using System.Net.WebSockets;

using System.Web.WebSockets;

1、首先创建一个ChatController继承自ApiController,如下代码:

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.WebSockets;
using WYC.DAL.DAL;namespace WYC.WebAPI.Controllers
{/// <summary>/// 聊天控制器/// </summary>[RoutePrefix("Chat")][AllowAnonymous] //不授权public class ChatController : ApiController{/// <summary>/// 记录客户端(根据用户ID一一对应)/// </summary>private static Dictionary<string, WebSocket> _listSockets = new Dictionary<string, WebSocket>();/// <summary>/// 业务逻辑层/// </summary>private readonly ChatDAL _dal = new ChatDAL();/// <summary>/// 连接/// </summary>/// <param name="userId">用户Id</param>/// <returns></returns>[Route("GetConnect"), HttpGet]public async Task<HttpResponseMessage> GetConnect(string userId){await _dal.UpdateUserOnlineState(userId, 1); //修改用户在线状态//指定执行不带参数的方法(注意:ProcessRequest(AspNetWebSocketContext context)这样属于不带参数,当前这个方法因为多了一个userId参数所以属于带参数方法)//HttpContext.Current.AcceptWebSocketRequest(ProcessRequest); //指定执行额外带参数的方法(注意:ProcessRequest(AspNetWebSocketContext context)这样属于不带参数,当前这个方法因为多了一个userId参数所以属于带参数方法)Func<AspNetWebSocketContext, Task> paramnter = async (context) =>{await ProcessRequest(context, userId);};HttpContext.Current.AcceptWebSocketRequest(paramnter);return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);}/// <summary>/// 客户端连接成功后执行方法(接收信息和发送信息)/// </summary>/// <param name="context">WebSocket上下文类</param>/// <param name="userId">当前连接用户Id</param>/// <returns></returns>public async Task ProcessRequest(AspNetWebSocketContext context, string userId){var socket = context.WebSocket;_listSockets.Add(userId, socket); //存放客户端连接的WebSocketwhile (true) //进入一个无限循环,一直监听当前连接成功的客户端操作行为{var buffer = new ArraySegment<byte>(new byte[1024]);//对web socket进行异步接收数据(相当于是等待客户端发送消息)var receivedResult = await socket.ReceiveAsync(buffer, System.Threading.CancellationToken.None);if (receivedResult.MessageType == WebSocketMessageType.Close) //客户端断开连接{await socket.CloseAsync(WebSocketCloseStatus.Empty, string.Empty, System.Threading.CancellationToken.None);//如果client发起close请求,对client进行ack_listSockets.Remove(userId); //删除await _dal.UpdateUserOnlineState(userId, 0); //修改用户离线状态break; //结束循环}if (socket.State == WebSocketState.Open) //客户端连接状态{//获取客户端发送消息内容string recvMsg = Encoding.UTF8.GetString(buffer.Array, 0, receivedResult.Count);//根据 前端|特殊字符 切割 获取接收信息用户Idstring receiptUserId = recvMsg.Substring(0, recvMsg.IndexOf("|"));recvMsg = recvMsg.Substring(recvMsg.IndexOf("|") + 1); //获取完整聊天信息await SendMessageToClientAsync(userId, receiptUserId, recvMsg); //一对一聊天}}}/// <summary>/// 发送消息给指定用户(一对一聊天)/// </summary>/// <param name="sendUserId">发送用户Id</param>/// <param name="receiptUserId">接受用户Id</param>/// <param name="message">发送消息</param>/// <returns></returns>public async Task SendMessageToClientAsync(string sendUserId, string receiptUserId, string message){//判断当前接受用户是否在词典里 并且 获取WebSocketif (_listSockets.TryGetValue(receiptUserId, out WebSocket client)) //存在 则实时推送消息给用户{if (client.State == WebSocketState.Open) //打开状态{byte[] buffer = Encoding.UTF8.GetBytes(message);ArraySegment<byte> segment = new ArraySegment<byte>(buffer);//发送对应的client用户消息await client.SendAsync(segment, WebSocketMessageType.Text, true, System.Threading.CancellationToken.None);}}//将聊天信息存放数据库await _dal.AddUserChatMessage(sendUserId, receiptUserId, message); //添加一对一聊天记录}/// <summary>/// 群发消息给用户(群发消息)/// </summary>/// <param name="message">消息</param>/// <returns></returns>public async Task SendMessageToAllAsync(string message){//这里因为WebSocket发送数据需要时字节格式 所以将字符串转换成字节,实际前端接收到的消息是字符串byte[] buffer = Encoding.UTF8.GetBytes(message);ArraySegment<byte> segment = new ArraySegment<byte>(buffer);//遍历所有在线用户并发送数据foreach (var client in _listSockets.Values){if (client.State == WebSocketState.Open) //打开状态{//发送对应的client用户消息await client.SendAsync(segment, WebSocketMessageType.Text, true, System.Threading.CancellationToken.None);}}}}
}

二、前端代码

需要引用jquery.js包如下代码:

<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-1.11.3.min.js"></script>var socket;//Socket连接
function Connection() {try {//192.168.1.7:1112把这个IP和端口替换成自己WebAPI项目IP和端口 其实这里跟Http协议一样的请求道理,无非就是把http://替换成了ws://var host = "ws://192.168.1.7:1112/Chat/GetConnect?userId=" + userId;socket = new WebSocket(host);/*连接*/socket.onopen = function (msg) {console.log("连接成功");};ReceptionMsg(); //接收消息} catch (e) {alert(e);}}//Socket发送消息
function SocketSendMsg(msg) {if (socket.readyState == WebSocket.OPEN) {//ltHyUserId:接收消息的用户Id   msg:消息  这里使用特殊字符|隔开,后端需要加工处理socket.send(ltHyUserId + "|" + msg);} else {alert('连接已关闭');}
}//Socket关闭连接
function SocketClose() {socket.close(); //关闭
}//Socket接收服务端消息
function ReceptionMsg() {/*接收消息*/socket.onmessage = function (evt) {console.log(evt.data); //接收到服务端发送过来的消息}
}

注意:

需要将IIS安装WebSocket协议,不管是服务器还是本机都需要,如下步骤:

1、打开控制面板

2、点击程序

3、点击启用和关闭Windows功能

4、勾选WebSocket协议

然后确认重启电脑就OK了。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网关的国际化改造

网关的国际化改造和web服务的改造有所不同。 问题 SpringCloud Gateway是基于reactor模型的&#xff0c;按照SpringBoot那套以及所尝试网上以及AI的i18n国际化方案&#xff0c;都没有成功。 解决问题 基本思路跟SpringBoot项目的i18n一样 通过MessageSource加载messages国际…

数据分析思维(五):分析方法——假设检验分析方法

数据分析并非只是简单的数据分析工具三板斧——Excel、SQL、Python&#xff0c;更重要的是数据分析思维。没有数据分析思维和业务知识&#xff0c;就算拿到一堆数据&#xff0c;也不知道如何下手。 推荐书本《数据分析思维——分析方法和业务知识》&#xff0c;本文内容就是提取…

5G学习笔记之Non-Public Network

目录 0. NPN系列 1. 概述 2. SNPN 2.1 SNPN概述 2.2 SNPN架构 2.3 SNPN部署 2.3.1 完全独立 2.3.2 共享PLMN基站 2.3.3 共享PLMN基站和PLMN频谱 3. PNI-NPN 3.1 PNI-NPN概述 3.2 PNI-NPN部署 3.2.1 UPF独立 3.2.2 完全共享 0. NPN系列 1. NPN概述 2. NPN R18 3. 【SNPN系列】S…

【专题】2024年悦己生活消费洞察报告汇总PDF洞察(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p38654 在当今时代背景下&#xff0c;社会发展日新月异&#xff0c;人们的生活方式与消费观念正经历深刻变革。MoonFox 月狐数据的《2024 年悦己生活消费洞察报告》聚焦于这一充满活力与变化的消费领域。随着就业、婚姻等社会压力的…

Latex+VsCode+Win10搭建

最近在写论文&#xff0c;overleaf的免费使用次数受限&#xff0c;因此需要使用本地的形式进行编译。 安装TEXLive 下载地址&#xff1a;https://mirror-hk.koddos.net/CTAN/systems/texlive/Images/ 下载完成直接点击iso进行安装操作。 安装LATEX Workshop插件 设置VsCode文…

模型 课题分离

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。明确自我与他人责任。 1 课题分离的应用 1.1课题分离在心理治疗中的应用案例&#xff1a;李晓的故事 李晓&#xff0c;一位28岁的软件工程师&#xff0c;在北京打拼。他面临着工作、家庭和感情的多重…

panddleocr-文本检测+文本方向分类+文本识别整体流程

panddleocr-文本检测文本方向分类文本识别整体流程 通过文本检测–>文本方向分类–>文本识别&#xff0c;即可识别出0~360度的旋转文本。 文本检测的最小外接矩形框根据长宽可以看到90度的角度&#xff0c;而再加入文本方向分类就能扩展到180度的角度。

练14:DFS基础

欢迎大家订阅【蓝桥杯Python每日一练】 专栏&#xff0c;开启你的 Python数据结构与算法 学习之旅&#xff01; 文章目录 1 DFS基础2 n重循环&#xff08;嵌套循环&#xff09;3 DFS与n重循环的区别与联系4 例题分析 1 DFS基础 ①定义 深度优先搜索&#xff08;DFS&#xff0c…

DataX与DataX-Web安装与使用

DataX github地址&#xff1a;DataX/introduction.md at master alibaba/DataX GitHub 环境准备 Linux环境系统 JDK&#xff08;1.8及其以上版本&#xff0c;推荐1.8&#xff09; Python&#xff08;2或者3都可以&#xff09; Apache Maven 3.x&#xff08;源码编译安装…

语音助手关键模块整理

常见的 ASR 技术和平台包括&#xff1a; Google Speech-to-Text&#xff1a;这是一个非常流行的 ASR 服务&#xff0c;提供高精度的语音转文本功能&#xff0c;广泛应用于各种语音助手和智能设备。 Microsoft Azure Speech&#xff1a;微软的语音服务&#xff0c;也包括 ASR 技…

Day13 用Excel表体验梯度下降法

Day13 用Excel表体验梯度下降法 用所学公式创建Excel表 用Excel表体验梯度下降法 详见本Day文章顶部附带资源里的Excel表《梯度下降法》&#xff0c;可以对照表里的单元格公式进行理解&#xff0c;还可以多尝试几次不同的学习率 η \eta η来感受&#xff0c;只需要更改学习率…

NACA四位数字翼型

NACA四位数字翼型&#xff0c;以NACA 2412为例 第一位数字2 —相对弯度 第二位数字4 —相对弯度所有位置&#xff08;单位化后的&#xff09; 最末两位数字12 —相对厚度 所有NACA四位数字翼型的&#xff08;相对厚度所在的位置&#xff09;

解锁动态规划的奥秘:从零到精通的创新思维解析(3)

解锁动态规划的奥秘&#xff1a;从零到精通的创新思维解析&#xff08;3&#xff09; 前言&#xff1a; 小编在前几日书写了关于动态规划习题的博客&#xff08;PS&#xff1a;其实这些都是我的存稿&#xff0c;我已经好久没写博客了截止到现在&#xff0c;确实摆烂&#xff…

UE5仿漫威争锋灵蝶冲刺技能

这两天玩了一下漫威争锋Marvel Rivals&#xff0c;发现是UE5做的&#xff0c;对里面一些角色技能挺感兴趣的&#xff0c;想简单复刻一下技能功能&#xff0c;顺便复习一下学过的知识 首先把摄像机设置调整一下 CameraBoom里搜索lag 把摄像机延迟关掉 &#xff0c;这样摄像机就…

算法题(13):异或变换

审题&#xff1a; 这题的数据量比较大&#xff0c;所以暴力解法肯定是过不了了&#xff0c;我们根据异或运算的性质来找找规律&#xff0c;不难发现他是有循环周期的。 最终我们的周期是一个不小于n的2的最小整数次幂。 疑问一&#xff1a;为什么会有循环&#xff1f; 1.因为这…

oracle: create new database

用database configuration Assistant 引导创建数据库。记得给system,sys 设置自己的口令&#xff0c;便于添加新操作用户。 创建操作用户&#xff1a; -- 别加双引号&#xff0c;否则&#xff0c;无法用 create user geovindu identified by 888888; create user geovin identi…

IntelliJ IDEA 快捷键大全:提升开发效率的利器

目录 一、基础快捷键 1. 文件操作快捷键 2. 编辑&#xff08;Editing&#xff09; 2.1 代码补全与导航 2.2 代码编辑 2.3 代码折叠与展开 3. 查找与替换 4. 调试 5. 版本控制 高级快捷键 重构快捷键&#xff1a;让代码更加优雅 导航快捷键&#xff1a;快速定位代码 …

GOC编程 第2课 简单命令---直走和转弯命令

第2课 简单命令---直走和转弯命令 goc电子课程https://www.51goc.com/static/gocDemo/lesson.html?options%C5%81%C4%98%C5%96%C5%9F%C5%89%C5%89%C5%95%C5%94%C5%B3%C5%9E%C4%98%C4%80%C4%98%C5%94%C5%9F%C5%8D%C4%88%C4%98%C5%87&winNamelesson2 2A 闯关 goc电子课程htt…

MFC/C++学习系列之简单记录9——简单加法

MFC/C学习系列之简单记录9——简单加法 前言界面设计控件添加添加变量添加事件 后台代码总结 前言 基本的一些使用已经了解&#xff0c;那么就做个简单的加法来练手吧&#xff01; 界面设计 控件添加 在工具箱中选择Edit control和Static Text两个控件&#xff0c;分别设置为…

AOP 面向切面编程的实现原理

AOP是基于IOC的Bean加载来实现的&#xff0c;所以理解Spring AOP的初始化必须要先理解Spring IOC的初始化。然后就能找到初始化的流程和aop对应的handler&#xff0c;即parseCustomElement方法找到parse aop:aspectj-autoproxy的handler(org.springframework.aop.config.AopNam…