Asp.Net WebApi使用Websocket
直接上代码

/// <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的更多相关文章
- C# WebApi+Task+WebSocket实战项目演练(四)
一.课程介绍 本次分享课程属于<C#高级编程实战技能开发宝典课程系列>中的第四部分,阿笨后续会计划将实际项目中的一些比较实用的关于C#高级编程的技巧分享出来给大家进行学习,不断的收集.整理 ...
- C#实战技能之WebApi+Task+WebSocket
一.背景介绍 环境的局限性: 用户在使用XX客户端的时候,必须每台电脑都安装打印组件,同时由于XX客户端使用的是 websocket进行通讯,这就必须限制用户的电脑浏览器必须是IE10.0+以上版本, ...
- 连表查询都用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%我不知道在 ...
- 重温ASP.NET WebAPI(一)初阶
重温ASP.NET WebAPI(一)初阶 前言 本文为个人对WebApi的回顾无参考价值.主要简单介绍WEB api和webapi项目的基本结构,并创建简单地webaapi项目实现CRUD操作. ...
- Asp.Net WebApi核心对象解析(下篇)
在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...
- ASP.NET WebApi OWIN 实现 OAuth 2.0
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...
- Asp.Net WebApi核心对象解析(上篇)
生活需要自己慢慢去体验和思考,对于知识也是如此.匆匆忙忙的生活,让人不知道自己一天到晚都在干些什么,似乎每天都在忙,但又好似不知道自己到底在忙些什么.不过也无所谓,只要我们知道最后想要什么就行.不管怎 ...
- ASP.NET WebApi 文档Swagger深度优化
本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明博客园蜗牛原文地址,cnblogs.com/tdws 写在前面 请原谅我这个标题党,写到了第100篇随笔,说是深度优化,其实也并没有什么深度 ...
- ASP.NET WebApi 文档Swagger中度优化
本文版权归博客园和作者吴双本人共同所有,转载和爬虫请注明原文地址:www.cnblogs.com/tdws 写在前面 在后台接口开发中,接口文档是必不可少的.在复杂的业务当中和多人对接的情况下,简 ...
随机推荐
- Acwing 245.你能回答这些问题吗
题目描述 给定长度为N的数列A,以及M条指令,每条指令可能是以下两种之一: 1."1 x y",查询区间 [x,y] 中的最大连续子段和,即 maxx≤l≤r≤y{∑ri=lA[i ...
- Java基础教程——注解
注解 JDK 5开始,Java支持注解. 注解,Annotation,是一种代码里的特殊标记,这些标记可以在编译.类加载.运行时被读取并执行,而且不改变原有的逻辑. 注解可以用于:生成文档.编译检查. ...
- Java集合【9】-- Vector源码解析
目录 1.Vector介绍 2. 成员变量 3. 构造函数 4. 常用方法 4.1 增加 4.2 删除 4.3 修改 4.4 查询 4.5 其他常用函数 4.6 Lambda表达式相关的方法 4.7 ...
- django搭建完毕运行显示hello django
1.使用pycharm打开工程,进入工程配置解释器路径 2.视图和url 视图:处理我们从业务的地方,可以理解为函数 url:进行路由匹配的地方,先在主工程bookpro中进行匹配,如果匹配ok,那么 ...
- [BUGCASE]层叠上下文和z-index属性使用不当引发的文本被遮挡的问题
一.问题描述 在一个fixed-data-table(一个React组件)制作的表格中,需要给表头的字段一个提示的特效,所以做了一个提示层 这个提示层被固定(拖动表格的水平滚动条时固定)的表格列遮住 ...
- 详解docker部署SpringBoot及如何替换jar包
关于docker的安装和使用,可以看看之前这两篇文章.Docker从安装部署到Hello World和Docker容器的使用和连接.这篇文章主要介绍如何在docker上部署springboot项目.关 ...
- Kafka探究之路-命令小结
操作kafka之前,要先启动安装好的zk ,因为kafka的数据都保存在zk中,zk相当于是kafka的数据库吧. 安装的zk kafka 一定要按照书上,网上的教程,将相应的配置文件全部改成自己的, ...
- jupyter notebook 将当前目录设置为工作目录
生成配置文件首先打开你的CMD或者是终端(Linux),在你配置过环境变量的基础下,你直接输入以下命令: jupyter notebook --generate-config 然后打开生成的配置文件, ...
- 题解-[WC2011]最大XOR和路径
[WC2011]最大XOR和路径 给一个 \(n\) 个点 \(m\) 条边(权值为 \(d_i\))的无向有权图,可能有重边和子环.可以多次经过一条边,求 \(1\to n\) 的路径的最大边权异或 ...
- 基于Dokcer搭建Redis集群搭建(主从集群)
最近陆陆续续有不少园友加我好友咨询 redis 集群搭建的问题,我觉得之前写的这篇 <基于Docker的Redis集群搭建> 文章一定是有问题了,所以我花了几分钟浏览之前的文章总结了下面几 ...