直接上代码

/// <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. LGOJ5022 旅行 noip tg 2018

    旅行 标签(空格分隔): noip2018 提高组 今天我给大家带来一份题解. 题目的大大致意思是这样的: $$ 有一颗 树/基环树 求最小遍历顺序 $$ 树的情况自然不必多讲.做一些末端的微处理(将 ...

  2. Visual Studio使用Git忽略不想上传到远程仓库的文件

    前言: 作为一个.NET开发者而已,有着宇宙最强IDE:Visual Studio加持,让我们的开发效率得到了更好的提升.我们不需要担心环境变量的配置和其他代码管理工具,因为VS有丰富的拓展工具.废话 ...

  3. 多态,向上转型,向下转型,final关键字

    多态 概述   多态封装性,继承性之后,面向对象的第三大特性. 定义   多态:是指同一种行为,具有多个不同的表现形式.   生活中,比如跑的动作,猫,狗,大象跑起来的动作都是不一样的,再比如飞的动作 ...

  4. 【译】用 React 和 D3 创建图表

    本文翻译自:https://dzone.com/articles/charts-with-modern-react-and-d3 本文将介绍如何利用 D3JS 和 ReactJS 来创建基础图表. R ...

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

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

  6. mq checkpoint文件

    记录comitlog,consumeQueue,Index文件的刷盘时间点,文件固定长度4k,其中只用该文件的24个字节,其存储格式: 8字节physicMsgtimestamp+8字节logicsM ...

  7. 【数据结构】关于前缀树(单词查找树,Trie)

    前缀树的说明和用途 前缀树又叫单词查找树,Trie,是一类常用的数据结构,其特点是以空间换时间,在查找字符串时有极大的时间优势,其查找的时间复杂度与键的数量无关,在能找到时,最大的时间复杂度也仅为键的 ...

  8. git的使用以及git和码云的连接

    什么是git? ​ Git是一个开源的分布式版本控制系统,可以有效.高速地处理从很小到非常大的项目版本管理.可以保存许多的历史版本,并且可以多人同时进行开发. 分布式版本控制系统 ​ 每个人拥有该项目 ...

  9. 等待多线程完成的CountDownLatch(带示例)

    开始磨刀霍霍向多线程了,这期是 CountDownLatch 的一个小示例. 定义:CountDownLatch 允许一个或多个线程等待其他线程完成操作. 应用需求举例:假设有4个线程,A.B.C.D ...

  10. 2019 ACM/ICPC North America Qualifier G.Research Productivity Index(概率期望dp)

    https://open.kattis.com/problems/researchproductivityindex 这道题是考场上没写出来的一道题,今年看看感觉简单到不像话,当时自己对于dp没有什么 ...