博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
asp.net 中长尾链接实现推送 -- comet
阅读量:5061 次
发布时间:2019-06-12

本文共 9131 字,大约阅读时间需要 30 分钟。

 

 一般需求推送服务时,都会去第三方拿推送组件,如”极光“,”百度“,”小米"什么的,自己用.net实现推送服务端需要面对很多问题,比如C10K,但是企业内部使用往往用不了10K的链接,有个1K,2K就足够,这个时候完全可以自己实现一个推送服务,这样手机应用就不用走外网了。

 

使用.net实现推送服务有几个选择

1.是使用WCF 基于TCP的回调-针对.net To .net 端,经过7*24小时测试,2K左右的链接能稳定hold住,参考:http://www.cnblogs.com/wdfrog/p/3924718.html

2.自己使用TcpListener或Socket实现一个长连击服务器,由于推送的信息都很短(长信息只推送信息编号),这样在一个MTU里面就可以完成传输,整体上实现起来也不麻烦 ,最近也做了个,大概加一起就400百来行代码,2K链接已经(实际只要hold住98个用户就好了)稳定运行了6天了。

3.使用Comet Request, 首先Comet Request 采用Asp.net + IIS,整整网页就好了,另外由于IIS应用程序池会定期回收,程序写的烂点也不影响,每天都给你一个新的开始,还有Comet 的实现网上代码很多,还有开源的SignalR可以用.

 

采用Comet方式的实现

1.服务端,使用IHttpAsyncHandler来Hold住请求,需要根据cookie或QueryString中带的UserId来区分不同的用户

using System;using System.Collections.Generic;using System.Linq;using System.Web;namespace MangoPush.WebComet.Core{    public class PushAsyncHandle : IHttpAsyncHandler    {        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)        {                        var ar = new PushAsyncResult(context, cb, extraData);            CometRequestMgr.Instance.Add(ar);            return ar;        }        public void EndProcessRequest(IAsyncResult result)        {                    }        public bool IsReusable        {            get { return true; }        }        public void ProcessRequest(HttpContext context)        {            throw new NotImplementedException();        }    }}
View Code
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Threading;namespace MangoPush.WebComet.Core{    public class PushAsyncResult:IAsyncResult    {        private static int GId = 0;        private bool m_IsCompleted = false;        private AsyncCallback Callback = null;        public HttpContext Context;        private Object _AsyncState;        public DateTime AddTime{
get;private set;} public int Id { get; set; } public PushAsyncResult(HttpContext context, AsyncCallback callback, object asyncState) { Context = context; Callback = callback; _AsyncState = asyncState; AddTime = DateTime.Now; Interlocked.Increment(ref GId); int userId = int.TryParse(Context.Request["UserId"], out userId) ? userId : 0; Id = userId; if (userId == 0) { Release(JUtil.ToJson(new {Code=-1,Msg="未提供UserId" })); } } public void Release(string msg) { try { try { Context.Response.Write(msg); } catch { } if (Callback != null) { Callback(this); } } catch { } finally { m_IsCompleted = true; } } public object AsyncState { get { return _AsyncState; } } public System.Threading.WaitHandle AsyncWaitHandle { get { return null; } } public bool CompletedSynchronously { get { return false; } } public bool IsCompleted { get { return m_IsCompleted; } } }}
View Code
using System;using System.Collections.Generic;using System.Collections.Concurrent;using System.Linq;using System.Web;namespace MangoPush.WebComet.Core{    public class CometRequestMgr    {        public static readonly CometRequestMgr Instance = new CometRequestMgr();        private ConcurrentDictionary
Queue = new ConcurrentDictionary
(); private CometRequestMgr() { var timer = new System.Timers.Timer(); timer.Interval = 1000 * 30; timer.AutoReset = false; timer.Elapsed += (s, e) => { try { var list = Queue.Select(ent => ent.Value).ToList(); #region 清理完成链接 foreach (var it in list) { if (it.IsCompleted) { try { PushAsyncResult c = null; Queue.TryRemove(it.Id,out c); } catch (Exception ex) { continue; } } } #endregion } catch (Exception ex) { } finally { timer.Start(); } }; timer.Start(); } public void Add(PushAsyncResult ar) { Queue[ar.Id] = ar; } public void BroadCastMsg(string msg) { msg +="," + DateTime.Now.ToString(); foreach (var c in Queue) { try { c.Value.Release(JUtil.ToJson( new {Code=0,Msg= msg})); } catch { } finally { PushAsyncResult outIt=null; Queue.TryRemove(c.Key,out outIt); } } } }}
View Code

2.网页端,使用ajax方式发起访问,特别注意的是需要设置timeout,这样如果断线或者服务端挂了重启,可以自动修复

$(function () {        function long_polling() {            var _url = '/pushService.act?userId=' + $("#userId").val();            console.log(_url);            var ajaxRequest = $.ajax({                url: _url,  //请求的URL                timeout: 1000 * 60 * 3, //超时时间设置,单位毫秒                type: 'get',  //请求方式,get或post                data: {},  //请求所传参数,json格式                dataType: 'json', //返回的数据格式                success: function (data) { //请求成功的回调函数                    console.log(data);                    if (data.Code == 0) {                        $('#msg').append(data.Msg + "
"); } }, complete: function (XMLHttpRequest, status) { //请求完成后最终执行参数 if (status == 'timeout') {
//超时,status还有success,error等值的情况 ajaxRequest.abort(); console.log("超时"); } if ($("#btn").val() == "停止") { long_polling(); } } }); } $("#btn").click(function () { if ($("#btn").val() == "启动") { long_polling(); $("#btn").val("停止"); } else { $("#btn").val("启动"); } }); $("#btnCls").click(function () { $("#msg").text(""); }); });
View Code

3.Winform端,采用WebClient发起请求,并且使用AutoResetEvent控制超时重连(相当于心跳包)

private void Do()        {            try            {                                while (Enable)                {                    Console.WriteLine("发起请求!");                    var url = @"http://192.168.9.6:9866/pushService.act?userId=18";                    using (var wc = new WebClient())                    {                        wc.Encoding = Encoding.UTF8;                        #region 回调处理                        wc.DownloadStringCompleted += (s, e) => {                                                  if (e.Error != null)                            {                                Console.WriteLine(e.Error);                            }                            else if (e.Cancelled)                            {                                Console.WriteLine("Be Cancelled!");                            }                            else                            {                                Console.WriteLine(e.Result);                                                            }                            autoReset.Set();                        };                        #endregion                              var uri = new Uri(url);                        wc.DownloadStringAsync(uri);                        var isOK= autoReset.WaitOne(1000 * 60 * 5);                        if (!isOK)                        {                            wc.CancelAsync();                        }                                                                    }                }            }            catch (Exception ex)            {                Console.WriteLine("错误" + ex.Message);            }            finally            {                if (Enable)                {                    ThreadPool.QueueUserWorkItem(o => { Do(); }, null);                }            }                        }
View Code

4.Android端

1.类似WinForm端,目前采用AsyncHttpClient,写法跟Js差不多,需要设置timeout

2.由于android会回收进程,需要AlarmManager,定期检查推送服务是否还存活

3.android系统重启,开关网络,调整时间,需要订阅相应广播,调整AlermManager,触发平率。

 

总结:

     整个能支持10K,100K,2000K链接的,挺不容易,但是一般中小企业使用1K,2K甚至0.1K足矣,3,4百行代码就完事,至少可以让员工不用连3G,4G了,NND,提速降价,也不知道降那去了。

最后整2个图片

转载于:https://www.cnblogs.com/wdfrog/p/4600166.html

你可能感兴趣的文章
第9课 uart
查看>>
Range和xrange的区别
查看>>
STL容器之vector
查看>>
无法向会话状态服务器发出会话状态请求
查看>>
数据中心虚拟化技术
查看>>
01入门
查看>>
复习文件操作
查看>>
SQL Server 使用作业设置定时任务之一(转载)
查看>>
第二阶段冲刺-01
查看>>
BZOJ1045 HAOI2008 糖果传递
查看>>
发送请求时params和data的区别
查看>>
JavaScript 克隆数组
查看>>
eggs
查看>>
一步步学习微软InfoPath2010和SP2010--第七章节--从SP列表和业务数据连接接收数据(4)--外部项目选取器和业务数据连接...
查看>>
如何增强你的SharePoint 团队网站首页
查看>>
FZU 1914 Funny Positive Sequence(线性算法)
查看>>
oracle 报错ORA-12514: TNS:listener does not currently know of service requested in connec
查看>>
基于grunt构建的前端集成开发环境
查看>>
MySQL服务读取参数文件my.cnf的规律研究探索
查看>>
java string(转)
查看>>