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 写在前面 在后台接口开发中,接口文档是必不可少的.在复杂的业务当中和多人对接的情况下,简 ... 
随机推荐
- Python_生成器和迭代器的区别
			迭代器和生成器的区别是什么?这个问题面试的时候经常作为灵魂拷问.今天一起从概念到代码梳理一遍,作为总结和记录. 区别是: 生成器的好处是延迟计算,一次返回一个结果.也就是说,它不会一次生成所有的结果, ... 
- Matlab 补充知识
			1:disp函数和fprintf函数类似 disp(a) 自动输出a变量的值 disp('dadad') 输出一个字符串 Disp(['a','b','ccc']) 三个字符串连在一起 2:matla ... 
- Java蓝桥杯——排列组合
			排列组合介绍 排列,就是指从给定n个数的元素中取出指定m个数的元素,进行排序. 组合,则是指从给定n个数的元素中仅仅取出指定m个数的元素,不考虑排序. 全排列(permutation) 以数字为例,全 ... 
- 实用主义当道——GitHub 热点速览 Vol.48
			作者:HelloGitHub-小鱼干 当你看到实用为本周的关键词时,就应该知道本周的 GitHub 热点霸榜的基本为高星老项目,例如:知名的性能测试工具 k6,让你能在预生产环境和 QA 环境中以高负 ... 
- DNS、IP地址、子网掩码和默认网关
			一.DNS服务器 DNS是指:域名服务器(Domain Name Server).在Internet上域名与IP地址之间是一一对应的,域名虽然便于人们记忆,但机器之间只能互相认识IP地址,它们之间的转 ... 
- 一万字详解 Redis Cluster Gossip 协议
			Redis Cluster Gossip 协议 大家好,我是历小冰,今天来讲一下 Reids Cluster 的 Gossip 协议和集群操作,文章的思维导图如下所示. 集群模式和 Gossip 简介 ... 
- 教学之Treap
			放在前面的话 本蒟蒻因为最近的题目总是搞点奇奇怪怪的平衡树,就去学了下\(Treap\) 现在来总结一下 由于本人是个蒟蒻,本文可能有部分错误,麻烦各位读者大佬在评论区提醒 什么是\(Treap\) ... 
- 第九章、Qt Designer可视化设计界面布局组件介绍
			老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 在Qt Designer中,在左边部件栏的提供了界面布局相关部件,如图: 可以看到共包含有 ... 
- PyQt(Python+Qt)学习随笔:Qt Designer中部件的geometry几何属性
			geometry属性保存部件相对于其父级对象的位置和大小,Qt实际上是以一个长方形来表示部件的位置和大小的,包括左上角的坐标位置.长度和宽带. 当部件的geometry调整时,部件如果可见将立即接收m ... 
- 总括订单Blanket order
			总括订单Blanket order是客户向其供应方发出的采购订单,但其中包含一段时间内的多个交货日期,通常使用谈判时的预定价格.大多数情况下,它用于对消耗性商品有经常性需求的情况.总括订单通常用于客户 ... 
