1.需求示意图
2.需求描述
原本是为了给做unity3d客户端开发的同事提供不定时的消息推送,比如商城购买道具后服务端将道具信息推送给客户端。
本篇文章简化理解,用“相关部门开展活动,向全市人民征集社会服务改善意见”为例子。但核心想法一致:单向推送。所以这个功能并不是聊天室,不需要客户端和客户端之间互相通信。核心界面只和服务端建立WebSocket连接,推送消息全部来自其他地方。
只有核心页面和服务端建立WebSocket连接,其他市民们都是通过web开发者耳熟能详的http协议在发送消息,不要以为是市民们和部门公告栏玩WebSocket互动。
3.代码如下,复制即可使用
①WebSocket帮助类,负责建立连接和推送消息
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets; namespace WSTest
{
public class WSHelper
{
/// <summary>
/// 保存客户端的WebSocket对象
/// </summary>
private static readonly Dictionary<string, WebSocket> dicSockets = new Dictionary<string, WebSocket>(); #region 构建线程安全的单例模式
private static WSHelper _instance;
private WSHelper()
{ } public static WSHelper GetInstance()
{
if (_instance == null)
{
lock (dicSockets)
{
if (_instance == null)
{
_instance = new WSHelper();
}
}
}
return _instance;
}
#endregion /// <summary>
/// 和客户端建立WebSocket连接
/// </summary>
/// <param name="arg">客户端发送的WebSocket相关信息</param>
/// <returns></returns>
public async Task ProcessWSChat(AspNetWebSocketContext arg)
{
// 1.获取请求的客户端WebSocket对象
WebSocket socket = arg.WebSocket;
// 2.获取自定义的参数
string adminUserKey = arg.QueryString["adminUserKey"];
if (string.IsNullOrEmpty(adminUserKey)) return;
// 3.将用户编号作为标识客户端唯一性的Key,保存客户端的WebSocket对象
dicSockets[adminUserKey] = socket; while (true)
{
ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[ * ]);
WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None); try
{
if (socket.State != WebSocketState.Open)
{
dicSockets.Remove(adminUserKey);
break;
}
}
catch
{
break;
}
}
} /// <summary>
/// 服务端向客户端推送消息
/// </summary>
public bool SendMsg(string message, string adminUserKey)
{
WebSocket socket = null;
if (dicSockets.ContainsKey(adminUserKey))
{
socket = dicSockets[adminUserKey];
}
else
{
return false;
} //【重要】执行下面socket.State代码可能会抛异常"无法访问已经释放的对象",
// 因为客户端已经处于断电、断网、强制关闭、刷新等状态,当前的WebSocket对象已经失去价值,直接删除即可
try
{
if (socket.State == WebSocketState.Open)
{
ArraySegment<byte> buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(message));
socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
return true;
}
}
catch
{
dicSockets.Remove(adminUserKey);
return false;
}
return false;
}
}
}
WSHelper
②webapi的控制器,负责建立WebSocket连接
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http; namespace WSTest.Controllers
{
[RoutePrefix("WebSocketConn")]
public class WebSocketConnController : ApiController
{
/// <summary>
/// 创建websocket连接
/// </summary>
[HttpGet]
[Route("GetConnect")]
public HttpResponseMessage GetConnect()
{
if (HttpContext.Current.IsWebSocketRequest)
{
HttpContext.Current.AcceptWebSocketRequest(WSHelper.GetInstance().ProcessWSChat);
}
return new HttpResponseMessage(HttpStatusCode.SwitchingProtocols);
}
}
}
WebSocketConnController
③webapi的业务控制器,征集意见
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http; namespace WSTest.Controllers
{
/// <summary>
/// 市民服务
/// </summary>
[RoutePrefix("CitizenService")]
public class CitizenServiceController : ApiController
{
/// <summary>
/// 市民意见征集
/// </summary>
[HttpGet]
[Route("GiveOpinion")]
public string GiveOpinion(string userName, string msg, string sendTo)
{
//1.发送消息给客户端
string sendMsg = string.Format("热心市民{0}有话要说:{1}", userName, msg);
bool result = WSHelper.GetInstance().SendMsg(sendMsg, sendTo); //2.接收结果,若发送失败,可能客户端还未成功连接WebSocket
return result ? "已提交,您可以去相关部门的官网查看刚发送的信息了。" : "相关部门的平台还没开放,请耐心等待"; }
}
}
CitizenServiceController
④测试用部门公告栏页面【核心页面】
<!DOCTYPE html>
<html>
<head>
<title>教育局的市民意见征集布告栏</title>
</head>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<body>
<div id="titleMsg"></div>
<div id="msgMenu">
来自市民的话:<br>
</div>
<script type="text/javascript">
var webSocket;
var msgCount = 1;
//HTTP处理程序的地址
var handlerUrl = "ws://localhost:2465/WebSocketConn/GetConnect?adminUserKey=adminA"; $(function(){
InitWebSocket();
});
function CloseWebSocket() {
webSocket.close();
webSocket = undefined;
} function InitWebSocket() {
//如果WebSocket对象未初始化,则初始化
if (webSocket == undefined) {
webSocket = new WebSocket(handlerUrl); //打开连接处理程序
webSocket.onopen = function () {
//WebSocket连接成功
$("#titleMsg").text("平台已开放,欢迎大家留言");
}; //消息数据处理程序
webSocket.onmessage = function (e) {
updMsgMenu(e.data);
}; //关闭事件处理程序
webSocket.onclose = function () {
//WebSocket断开连接
}; //错误事件处理程序
webSocket.onerror = function (e) {
updMsgMenu(e.message);
};
}
else {
//webSocket.open();没有open方法
}
} function updMsgMenu(str){
var tempStr = $("#msgMenu").html();
tempStr = tempStr + msgCount + "." + str + "</br>";
msgCount++;
$("#msgMenu").html(tempStr);
} function Clear(){
msgCount = 1;
$("#msgMenu").html("消息列表:<br>");
} </script>
</body>
</html>
部门公告栏页面
⑤测试用市民意见征集页面
<!DOCTYPE html>
<html>
<head>
<title>市民意见征集平台</title>
</head>
<body>
您的姓名:<input type="text" id="userName" /><br>
您的意见:<textarea type="text" id="msg"></textarea><br>
您想给哪个部门留言:<select id="sendTo">
<option value="adminA">教育局</option>
<option value="adminB">社保局</option>
<option value="adminC">劳动局</option>
</select>
<input type="button" value="提交" onclick="doSend()" /> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script>
var msgCount = 1;
function doSend(){
$.ajax({
url: "http://localhost:2465/CitizenService/GiveOpinion",
type: "GET",
data:{
userName: $("#userName").val(),
msg: $("#msg").val(),
sendTo: $("#sendTo").val()
},
cache: false,
dataType: "json",
success: function (res) {
console.log(res);
alert("收到消息:"+ res);
},
error: function (error) {
alert("服务端繁忙");
}
});
}
</script>
</body>
</html>
市民意见征集页面
4.运行如下
①教育部门开放了自己的平台,准备接收市民意见
②有市民向教育部门反馈问题
③公告栏收到及时推送的消息
5.总结
①本案例中标识建立WebSocket连接的客户端唯一性的是自定义参数,但WebSocket内部标识唯一性的是SecWebSocketKey,可参考https://www.jianshu.com/p/8759dda1dbfc 了解原理。
因此只要核心页面断开了WebSocket连接(断点、断网、重启、刷新页面等),这次的WebSocket对象都不再有效,需要重新建立连接。
②本案例的需求是市民们向部门反应意见,不是聊天室。并且市民们反应意见是传统的http请求,服务端向核心页面推送消息才用到WebSocket。
③WSHelper.cs类中建立了线程安全的单例模式,目的是让所有用户访问到的保存WebSocket对象的字典集合对象唯一,如果不这样做,那么只有在发布公告栏那台电脑上才有用。
④案例缺点:服务端没有监听核心页面的WebSocket状态,因此只有在try-catch捕获异常时才可以处理掉客户端的WebSocket对象,对性能上有些不友好。
结合实际需求,在webapi内利用WebSocket建立单向的消息推送平台,让A页面和服务端建立WebSocket连接,让其他页面可以及时给A页面推送消息的更多相关文章
-
关于消息推送的补充,主要介绍服务端的实现,包含object c 版本 c 版本 java 版本 php 版本 (转)
要实现消息推送功能,我们可以采用第三方(腾讯:信鸽:百度:云推送:极光推送:友盟):当然,因为各种原因,我们不能使用第三方的推送服务,那我们就需要自己编写服务端.在网上寻觅了很久,找到一篇很不错的讲解 ...
-
iOS 微信消息拦截插件系列教程-附录(服务端成果展示)
微信iOS消息拦截插件教程 标签: 越狱开发 背景介绍 本教程所有内容免费 本教程来源于一次知识分享,如果有需要了解更多的 请联系QQ:480071411 iOS逆向高级开发群:375024882 服 ...
-
5G信令(就是用户身份信息)风暴——就是客户端通过公钥加密的消息(携带手机IMSI号)发给服务端,服务器需用私钥解密,这个解密比较消耗资源,如果短时间大量请求到来就会触发信令风暴
信令:手机开机后,先从USIM中读取之前运营商分配的临时身份信息GUTI/TMSI,发送携带该身份信息的信令给基站,请求接入运营商网络. 如果每个设备的每条消息都需要单独认证,则网络侧安全信令的验证需 ...
-
利用WebSocket和EventSource实现服务端推送
可能有很多的同学有用 setInterval 控制 ajax 不断向服务端请求最新数据的经历(轮询)看下面的代码: setInterval(function() { $.get('/get/data- ...
-
使用SignalR实现服务端消息推送
概述 这篇文章参考的是Server Broadcast with SignalR 2这篇教程,很不错的一篇教程,如果有兴趣的话可以查看原文,今天记录下来作为一个学习笔记,这样今后翻阅会更方便一点. 这 ...
-
Asp.net SignalR 实现服务端消息推送到Web端
之前的文章介绍过Asp.net SignalR, ASP .NET SignalR是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信. 今天我 ...
-
java服务端集成极光消息推送--详细开发步骤
1.极光推送账号准备 要使用极光消息推送必须先在官方网站上注册账号,并添加应用. 产品介绍:https://docs.jiguang.cn/jpush/guideline/intro/ 注册开发者账号 ...
-
Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!
随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...
-
C#服务端通过Socket推送数据到Android端App中
需求: 描述:实时在客户端上获取到哪些款需要补货. 要求: 后台需要使用c#,并且哪些需要补货的逻辑写在公司框架内,客户端采用PDA(即Android客户端 版本4.4) . 用户打开了补货通知页面时 ...
随机推荐
-
request_irq() | 注册中断服务函数【转】
本文转载自:http://blog.csdn.net/wealoong/article/details/7566546#t0 参考 : ARM Linux 中断机制分析.pdf linux-2.6. ...
-
Linux进程含义知多少
理想情况下,您应该明白在您的系统中运行的每一个进程.要获得所有进程的列表,可以执行命令 ps -ef(POSIX 风格)或 ps ax(BSD 风格).进程名有方括号的是内核级的进程,执行辅助功能(比 ...
-
怎么取消 Windows Server 2012 r2 RDP 限制每个用户只能进行一个会话(转)
在 Windows Server 2008 / 2008 R2 上,如果希望多个远程用户使用同一个账号同时访问服务器的 Remote Desktop(RDP),只需通过管理工具-远程桌面下的“远程桌面 ...
-
Cocos2d-android (05) 渐变动画(颜色,淡入淡出。。。)
淡入淡出.颜色渐变及动作重复执行 import org.cocos2d.actions.base.CCRepeatForever; import org.cocos2d.actions.interva ...
-
[转载+原创]Emgu CV on C# (六) —— Emgu CV on Canny边缘检测
Canny边缘检测也是一种边缘检测方法,本文介绍了Canny边缘检测的函数及其使用方法,并利用emgucv方法将轮廓检测解算的结果与原文进行比较. 图像的边缘检测的原理是检测出图像中所有灰度值变化较大 ...
-
[docker]docker的四种网络方式
声明: 本博客欢迎转发,但请保留原作者信息! 博客地址:http://blog.csdn.net/halcyonbaby 内容系本人学习.研究和总结,如有雷同,实属荣幸! bridge方式(默认) H ...
-
企业级分布式存储应用与实战-mogilefs实现
Mogilefs是什么 MogileFS是一个开源的分布式文件存储系统,由LiveJournal旗下的Danga Interactive公司开发.Danga团队开发了包括 Memcached.Mogi ...
-
JS实现将数字金额转换为大写人民币汉字
function convertCurrency(money) { //汉字的数字 var cnNums = new Array('零', '壹', '贰', '叁', '肆', '伍', '陆', ...
-
linux svn 开机启动
在/etc/init.d中建立svnboot,内容如下: #!/bin/bash if [ ! -f "/usr/bin/svnserve" ] then echo "s ...
-
1《想成为黑客,不知道这些命令行可不行》(Learn Enough Command Line to Be Dangerous)——基础(Basics)
基础 正如著名作者Neal Stephenson所说的那样,'(开发, 译者加)开始是命令行',尽管通过用户图形界面使用计算机及其简单,但是在许多场景中,最有效.最灵活地与计算机交互的方式是使用命令行 ...