直接上代码

/// <summary>
/// WebSocket Handler
/// </summary>
public class QWebSocketHandler
{
private WebSocket _websocket;
/// <summary>
/// 用户名
/// </summary>
public string User { get; set; }
/// <summary>
/// webSocket 连接关闭
/// </summary>
public event EventHandler Closed;
/// <summary>
/// webSocket 连接接受信息
/// </summary>
public event EventHandler<string> Received;
/// <summary>
/// webSocket 连接成功
/// </summary>
public event EventHandler<string> Opened;
/// <summary>
/// webSocket 请求连接
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task ProcessRequest(AspNetWebSocketContext context)
{
_websocket = context.WebSocket; var login = context.User.Identity.Name;
User = login;
Opened?.Invoke(this, login);
while(true)
{
var buffer = new ArraySegment<byte>(new byte[1024]);
var receivemsg = await _websocket.ReceiveAsync(buffer, System.Threading.CancellationToken.None); if(receivemsg.MessageType == WebSocketMessageType.Close)
{
await _websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "connect colsed", CancellationToken.None);
Closed?.Invoke(this, EventArgs.Empty);
break; }
if(_websocket.State == WebSocketState.Open)
{
string remsg = Encoding.UTF8.GetString(buffer.Array, 0, receivemsg.Count);
Received?.Invoke(this, remsg);
}
}
}
/// <summary>
/// 向当前连接发送消息
/// </summary>
/// <param name="msg">消息内容</param>
/// <returns></returns>
public async Task<bool> SendMSG(string msg)
{
if (_websocket == null || _websocket.State != WebSocketState.Open)
{
throw new Exception("the web socket is not connected");
}
var sebyte = Encoding.UTF8.GetBytes(msg);
var sebuffer = new ArraySegment<byte>(sebyte); await _websocket.SendAsync(sebuffer, WebSocketMessageType.Text, true, CancellationToken.None);
return true;
}
/// <summary>
/// 关闭当前webSocket连接
/// </summary>
public void Close()
{
if (_websocket == null || _websocket.State == WebSocketState.Closed || _websocket.State == WebSocketState.Aborted)
{
return;
} _websocket.Abort();
}
}
/// <summary>
/// 用户离线消息池
/// </summary>
public class MessagePool
{
/// <summary>
/// 用户
/// </summary>
public string User { get; set; }
/// <summary>
/// 消息集合
/// </summary>
public ConcurrentQueue<OffMessage> MessageS { get; set; } }
/// <summary>
/// 用户离线消息
/// </summary>
public class OffMessage:MessageTemplate
{
/// <summary>
/// 消息失效时间
/// </summary>
public DateTime ValidTime { get; set; } } /// <summary>
/// 消息实体
/// </summary>
public class MessageTemplate
{
/// <summary>
/// 接受消息的用户Login
/// </summary>
public string ToUser { get; set; }
/// <summary>
/// 发送消息的用户Login
/// </summary>
public string FromUser { get; set; }
/// <summary>
/// 消息内容
/// </summary>
public MessageContent MsgContent { get; set; }
} /// <summary>
/// 消息内容体实体模型
/// </summary>
public class MessageContent
{
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 日期
/// </summary>
public DateTime Time { get; set; }
}

handler

/// <summary>
/// webSocket服务
/// </summary>
public class QWebSocketService
{
private static ConcurrentDictionary<string, QWebSocketHandler> _websockets = new ConcurrentDictionary<string, QWebSocketHandler>();
/// <summary>
/// 用户离线消息池
/// 用户上线直接发送消息给用户
/// 离线消息仅保留3天,72小时
/// </summary>
private static ConcurrentQueue<MessagePool> UserMessageS = new ConcurrentQueue<MessagePool>();
/// <summary>
/// 连接websocket
/// </summary>
/// <param name="Login"></param>
/// <param name="Token"></param>
/// <returns></returns>
public static HttpResponseMessage Connect(System.Web.HttpContext context, string Login)
{ //如果用户存在于连接池则更新 webSocket连接信息,否则新建连接池 var handler = new QWebSocketHandler();
handler.Received -= Socket_Received;
handler.Received += Socket_Received; handler.Closed -= Socket_Closed;
handler.Closed += Socket_Closed; handler.Opened -= Socket_Opened;
handler.Opened += Socket_Opened; if (_websockets.Keys.Contains(Login))
{
var inhandler = _websockets[Login];
inhandler.Close();
_websockets[Login] = handler;
}
else
{
_websockets.TryAdd(Login, handler);
}
context.User = new System.Security.Principal.GenericPrincipal(new System.Security.Principal.GenericIdentity(Login), null); context.AcceptWebSocketRequest(handler); return new HttpResponseMessage(System.Net.HttpStatusCode.SwitchingProtocols); } /// <summary>
/// 清理过期消息
/// </summary>
private static void ClearUserMessage()
{
var validuser = new ConcurrentQueue<MessagePool>(); foreach (var msg in UserMessageS)
{
var valid = new ConcurrentQueue<OffMessage>(); foreach (var msgcontent in msg.MessageS)
{
if ((DateTime.Now - msgcontent.ValidTime).TotalHours < 72)
{
valid.Enqueue(msgcontent);
} }
msg.MessageS = valid;
if (!valid.IsEmpty)
{
validuser.Enqueue(msg);
}
}
UserMessageS = validuser; }
/// <summary>
/// Insert send to offline user's message in messagepool
/// </summary>
/// <param name="msg"></param>
private static void AddUserMessage(MessageTemplate msg)
{
if (UserMessageS.Any(q => q.User == msg.ToUser))
{
//存在离线用户离线消息
var innermsg = UserMessageS.FirstOrDefault(q => q.User == msg.ToUser);
OffMessage offmessage = new OffMessage()
{
ToUser = msg.ToUser,
FromUser = msg.FromUser,
MsgContent = msg.MsgContent,
ValidTime = DateTime.Now
};
innermsg.MessageS.Enqueue(offmessage);
}
else
{
//不存在离线用户消息
OffMessage offMessage = new OffMessage()
{
MsgContent = msg.MsgContent,
FromUser = msg.FromUser,
ToUser = msg.ToUser,
ValidTime = DateTime.Now
};
ConcurrentQueue<OffMessage> msgs = new ConcurrentQueue<OffMessage>();
msgs.Enqueue(offMessage);
MessagePool usermessage = new MessagePool()
{
User = msg.ToUser,
MessageS = msgs
}; UserMessageS.Enqueue(usermessage); } } private static async Task SendOffMessage(QWebSocketHandler socket, string login)
{
//有离线消息则发送
await Task.Delay(2000); //异步等待2秒发送离线消息 var msgs = UserMessageS.FirstOrDefault(q => q.User == login);
if (msgs != null)
{
var sended = new ConcurrentQueue<OffMessage>();
foreach (var omsg in msgs.MessageS)
{
var send = await socket.SendMSG(omsg.MsgContent.ToString()); if (!send)
{
send.Equals(omsg);
}
}
msgs.MessageS = sended; } ClearUserMessage();//清理过期离线消息
}
/// <summary>
/// 向指定用户发送消息
/// </summary>
/// <param name="Login"></param>
/// <param name="msg"></param>
/// <returns></returns>
public static async Task<bool> SendMSG(MessageTemplate msg)
{
if (_websockets.Any(q => q.Key == msg.ToUser))
{
var socket = _websockets[msg.ToUser];
if (socket == null)
{
//用户不在线,消息加入离线
AddUserMessage(msg);
return false;
}
var str = JsonConvert.SerializeObject(msg.MsgContent);
return await socket.SendMSG(str);
}
else
{
//用户不在线,消息加入离线
AddUserMessage(msg);
return false;
} } private static void Socket_Opened(object sender, string login)
{
//连接后,发送离线消息
SendOffMessage((QWebSocketHandler)sender, login);
}
/// <summary>
/// webSocket 接收消息
/// </summary>
/// <param name="sender"></param>
/// <param name="msg"></param>
private static void Socket_Received(object sender, string msg)
{ }
/// <summary>
/// webSocket 客户端关闭
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void Socket_Closed(object sender, EventArgs e)
{
var socket = (QWebSocketHandler)sender;
var csocket = _websockets.FirstOrDefault(q => q.Value == socket); _websockets.TryRemove(csocket.Key, out socket); }
} public static class HttpContextExtension
{
public static void AcceptWebSocketRequest(this HttpContext context, QWebSocketHandler handler)
{
context.AcceptWebSocketRequest(handler.ProcessRequest);
}
}

Service

/// <summary>
/// webSocket 消息管理,
/// 请使用WebSocket协议请求:
/// ws://server/api/msger/{login}/{token}
/// {login}为当前用户名;{token}为当前用户登陆的有效token
/// </summary>
[RoutePrefix("api/msger")]
public class MessageController : LoanAPI
{
private DBContext db = new DBContext();
/// <summary>
/// 请求webSocket连接
/// </summary>
/// <param name="login"></param>
/// <param name="token"></param>
/// <returns></returns>
[HttpGet]
[Route("connect/{login}/{token}")]
[AllowAnonymous]
public HttpResponseMessage Connect(string login, string token)
{
var user = db.SYS_User.FirstOrDefault(q => q.Login == login && q.Token == token);
if (user == null)
{
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Login is Not Valid");
}
else
{
if(HttpContext.Current.IsWebSocketRequest)
{
return QWebSocketService.Connect(HttpContext.Current, login);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.MethodNotAllowed, "Is Not WebSocekt Request");
} }
}
/// <summary>
/// 向用户发送消息,正常的http请求
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
[HttpPost]
[Route("send")]
public async System.Threading.Tasks.Task<ActionResult<bool>> SendMSGAsync([FromBody] MessageTemplate msg)
{ var sended = await QWebSocketService.SendMSG(msg); return new ActionResult<bool>(sended);
} }

Controller

js Common

(function ($) {

    $.config = {
url: '', //链接地址
token: '',// 通讯key
}; $.init = function (config) {
this.config = config;
return this;
}; /**
* 连接webcocket
*/
$.connect = function () {
var protocol = (window.location.protocol === 'http:') ? 'ws:' : 'wss:';
this.host = protocol + this.config.url;
this.protocols = this.config.token; window.WebSocket = window.WebSocket || window.MozWebSocket;
if (!window.WebSocket) { // 检测浏览器支持
this.error('Error: WebSocket is not supported .');
return;
}
this.socket = new WebSocket(this.host, [this.protocols]); // 创建连接并注册响应函数
this.socket.onopen = function () {
$.onopen();
};
this.socket.onmessage = function (message) {
$.onmessage(message);
};
this.socket.onclose = function () {
$.onclose();
$.socket = null; // 清理
};
this.socket.onerror = function (errorMsg) {
$.onerror(errorMsg);
}
return this;
} /**
* 自定义异常函数
* @param {Object} errorMsg
*/
$.error = function (errorMsg) {
this.onerror(errorMsg);
} /**
* 消息发送
*/
$.send = function (message) {
if (this.socket) {
this.socket.send(message);
return true;
}
this.error('please connect to the server first !!!');
return false;
} $.close = function () {
if (this.socket !== undefined && this.socket !== null) {
this.socket.close();
} else {
this.error("this socket is not available");
}
} /**
* 消息回調
* @param {Object} message
*/
$.onmessage = function (message) {
console.log(message)
} /**
* 链接回调函数
*/
$.onopen = function () {
console.log('连接成功')
} /**
* 关闭回调
*/
$.onclose = function () {
console.log('连接关闭');
} /**
* 异常回调
*/
$.onerror = function (error) {
console.log(error);
} })(ws = {});

Asp.Net WebApi使用Websocket的更多相关文章

  1. C# WebApi+Task+WebSocket实战项目演练(四)

    一.课程介绍 本次分享课程属于<C#高级编程实战技能开发宝典课程系列>中的第四部分,阿笨后续会计划将实际项目中的一些比较实用的关于C#高级编程的技巧分享出来给大家进行学习,不断的收集.整理 ...

  2. C#实战技能之WebApi+Task+WebSocket

    一.背景介绍 环境的局限性: 用户在使用XX客户端的时候,必须每台电脑都安装打印组件,同时由于XX客户端使用的是 websocket进行通讯,这就必须限制用户的电脑浏览器必须是IE10.0+以上版本, ...

  3. 连表查询都用Left Join吧 以Windows服务方式运行.NET Core程序 HTTP和HTTPS的区别 ASP.NET SignalR介绍 asp.net—WebApi跨域 asp.net—自定义轻量级ORM C#之23中设计模式

    连表查询都用Left Join吧   最近看同事的代码,SQL连表查询的时候很多时候用的是Inner Join,而我觉得对我们的业务而言,99.9%都应该使用Left Join(还有0.1%我不知道在 ...

  4. 重温ASP.NET WebAPI(一)初阶

    重温ASP.NET WebAPI(一)初阶   前言 本文为个人对WebApi的回顾无参考价值.主要简单介绍WEB api和webapi项目的基本结构,并创建简单地webaapi项目实现CRUD操作. ...

  5. Asp.Net WebApi核心对象解析(下篇)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  6. ASP.NET WebApi OWIN 实现 OAuth 2.0

    OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...

  7. Asp.Net WebApi核心对象解析(上篇)

    生活需要自己慢慢去体验和思考,对于知识也是如此.匆匆忙忙的生活,让人不知道自己一天到晚都在干些什么,似乎每天都在忙,但又好似不知道自己到底在忙些什么.不过也无所谓,只要我们知道最后想要什么就行.不管怎 ...

  8. ASP.NET WebApi 文档Swagger深度优化

    本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明博客园蜗牛原文地址,cnblogs.com/tdws   写在前面 请原谅我这个标题党,写到了第100篇随笔,说是深度优化,其实也并没有什么深度 ...

  9. ASP.NET WebApi 文档Swagger中度优化

    本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明原文地址:www.cnblogs.com/tdws   写在前面 在后台接口开发中,接口文档是必不可少的.在复杂的业务当中和多人对接的情况下,简 ...

随机推荐

  1. Jmeter测试TCP协议

    最近做了个项目是TCP协议传输数据,于是马上想试试JMeter测试TCP. 1首先需要下载第三方的TCP插件包ApacheJMeter_tcp.jar,下载步骤请自行百度. 2.下载完重新配置jmet ...

  2. Java基础教程——抽象类

    抽象类 抽象类是介于普通类(class)和接口(interface)之间的一种特殊类. 接口的方法都不实现,类的方法都必须实现,抽象类里的方法可以实现,可以不实现. Java 8之后接口中可以实现方法 ...

  3. Kafka入门之broker-消息设计

    消息设计 1.消息格式 Kafka的实现方式本质上是使用java NIO的ByteBuffer来保存消息,同时依赖文件系统提供的页缓存机制,而非依靠java的堆缓存. 2.版本变迁 0.11.0.0版 ...

  4. Jmeter(三十) - 从入门到精通 - Jmeter Http协议录制脚本工具-Badboy3(详解教程)

    1.简介 Badboy为方便自动化数据灵活性,以及脚本的重用,减少工作量:为此提供了脚本参数化的功能,这一篇文章宏哥以度娘搜索的关键字"北京-宏哥"进行参数化为例,宏哥带领你们实战 ...

  5. LaTeX相关自学文档

    install-latex-guide-zh-cn: lshort-zh-cn: 百度网盘链接:https://pan.baidu.com/s/1cBv9Fu8KFaf0QFZ7_slxmw 提取码: ...

  6. 03-Python里字符串的常用操作方法二

    1.lstrip():删除左侧空白字符 实例: my_str = ' hello world and my and test and python ' # 原始字符串 print(my_str) # ...

  7. Vite ❤ Electron——基于Vite搭建Electron+Vue3的开发环境【一】

    背景 目前社区两大Vue+Electron的脚手架:electron-vue和vue-cli-plugin-electron-builder, 都有这样那样的问题,且都还不支持Vue3,然而Vue3已 ...

  8. Sharding-JDBC分片策略详解(二)

    一.分片策略 https://shardingsphere.apache.org/document/current/cn/features/sharding/concept/sharding/ Sha ...

  9. [翻译自官方]什么是RDB和AOF? 一文了解Redis持久化!

    ​概述 本文提供Redis持久化技术说明,  建议所有Redis用户阅读. 如果您想更深入了解Redis持久性原理机制和底层持久性保证, 请参考文章 揭秘Redis持久化: http://antire ...

  10. moviepy音视频开发:音频合成类AudioArrayClip介绍

    ☞ ░ 前往老猿Python博文目录 ░ AudioArrayClip类是AudioClip的直接子类,用于从一个numpy音频数组构建音频剪辑.AudioArrayClip类只有一个构造方法,在构造 ...