一般需求推送服务时,都会去第三方拿推送组件,如”极光“,”百度“,”小米"什么的,自己用.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来区分不同的用户

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5.  
  6. namespace MangoPush.WebComet.Core
  7. {
  8. public class PushAsyncHandle : IHttpAsyncHandler
  9. {
  10. public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
  11. {
  12.  
  13. var ar = new PushAsyncResult(context, cb, extraData);
  14. CometRequestMgr.Instance.Add(ar);
  15. return ar;
  16. }
  17.  
  18. public void EndProcessRequest(IAsyncResult result)
  19. {
  20.  
  21. }
  22.  
  23. public bool IsReusable
  24. {
  25. get { return true; }
  26. }
  27.  
  28. public void ProcessRequest(HttpContext context)
  29. {
  30. throw new NotImplementedException();
  31. }
  32. }
  33. }
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Threading;
  6.  
  7. namespace MangoPush.WebComet.Core
  8. {
  9. public class PushAsyncResult:IAsyncResult
  10. {
  11. private static int GId = ;
  12.  
  13. private bool m_IsCompleted = false;
  14. private AsyncCallback Callback = null;
  15. public HttpContext Context;
  16. private Object _AsyncState;
  17. public DateTime AddTime{get;private set;}
  18. public int Id { get; set; }
  19.  
  20. public PushAsyncResult(HttpContext context, AsyncCallback callback, object asyncState)
  21. {
  22. Context = context;
  23. Callback = callback;
  24. _AsyncState = asyncState;
  25. AddTime = DateTime.Now;
  26. Interlocked.Increment(ref GId);
  27. int userId = int.TryParse(Context.Request["UserId"], out userId) ? userId : ;
  28. Id = userId;
  29. if (userId == )
  30. {
  31. Release(JUtil.ToJson(new {Code=-,Msg="未提供UserId" }));
  32. }
  33. }
  34. public void Release(string msg)
  35. {
  36. try
  37. {
  38. try
  39. {
  40. Context.Response.Write(msg);
  41. }
  42. catch { }
  43. if (Callback != null)
  44. {
  45. Callback(this);
  46. }
  47. }
  48. catch { }
  49. finally
  50. {
  51. m_IsCompleted = true;
  52. }
  53. }
  54. public object AsyncState
  55. {
  56. get { return _AsyncState; }
  57. }
  58.  
  59. public System.Threading.WaitHandle AsyncWaitHandle
  60. {
  61. get { return null; }
  62. }
  63.  
  64. public bool CompletedSynchronously
  65. {
  66. get { return false; }
  67. }
  68.  
  69. public bool IsCompleted
  70. {
  71. get { return m_IsCompleted; }
  72. }
  73. }
  74. }
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Concurrent;
  4. using System.Linq;
  5. using System.Web;
  6.  
  7. namespace MangoPush.WebComet.Core
  8. {
  9. public class CometRequestMgr
  10. {
  11.  
  12. public static readonly CometRequestMgr Instance = new CometRequestMgr();
  13. private ConcurrentDictionary<int, PushAsyncResult> Queue = new ConcurrentDictionary<int, PushAsyncResult>();
  14.  
  15. private CometRequestMgr()
  16. {
  17. var timer = new System.Timers.Timer();
  18. timer.Interval = * ;
  19. timer.AutoReset = false;
  20. timer.Elapsed += (s, e) =>
  21. {
  22.  
  23. try
  24. {
  25.  
  26. var list = Queue.Select(ent => ent.Value).ToList();
  27. #region 清理完成链接
  28. foreach (var it in list)
  29. {
  30. if (it.IsCompleted)
  31. {
  32. try
  33. {
  34. PushAsyncResult c = null;
  35. Queue.TryRemove(it.Id,out c);
  36.  
  37. }
  38. catch (Exception ex)
  39. {
  40.  
  41. continue;
  42. }
  43. }
  44. }
  45. #endregion
  46.  
  47. }
  48. catch (Exception ex)
  49. {
  50.  
  51. }
  52. finally
  53. {
  54. timer.Start();
  55. }
  56.  
  57. };
  58. timer.Start();
  59.  
  60. }
  61.  
  62. public void Add(PushAsyncResult ar)
  63. {
  64. Queue[ar.Id] = ar;
  65. }
  66. public void BroadCastMsg(string msg)
  67. {
  68. msg +="," + DateTime.Now.ToString();
  69. foreach (var c in Queue)
  70. {
  71. try
  72. {
  73.  
  74. c.Value.Release(JUtil.ToJson( new {Code=,Msg= msg}));
  75.  
  76. }
  77. catch { }
  78. finally
  79. {
  80. PushAsyncResult outIt=null;
  81. Queue.TryRemove(c.Key,out outIt);
  82. }
  83. }
  84. }
  85. }
  86. }

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

  1. $(function () {
  2.  
  3. function long_polling() {
  4. var _url = '/pushService.act?userId=' + $("#userId").val();
  5. console.log(_url);
  6.  
  7. var ajaxRequest = $.ajax({
  8. url: _url, //请求的URL
  9. timeout: 1000 * 60 * 3, //超时时间设置,单位毫秒
  10. type: 'get', //请求方式,get或post
  11. data: {}, //请求所传参数,json格式
  12. dataType: 'json', //返回的数据格式
  13. success: function (data) { //请求成功的回调函数
  14.  
  15. console.log(data);
  16. if (data.Code == 0) {
  17. $('#msg').append(data.Msg + "<br/>");
  18. }
  19. },
  20. complete: function (XMLHttpRequest, status) { //请求完成后最终执行参数
  21. if (status == 'timeout') {//超时,status还有success,error等值的情况
  22. ajaxRequest.abort();
  23. console.log("超时");
  24. }
  25.  
  26. if ($("#btn").val() == "停止") {
  27. long_polling();
  28. }
  29. }
  30. });
  31.  
  32. }
  33. $("#btn").click(function () {
  34.  
  35. if ($("#btn").val() == "启动") {
  36. long_polling();
  37. $("#btn").val("停止");
  38. } else {
  39. $("#btn").val("启动");
  40. }
  41. });
  42.  
  43. $("#btnCls").click(function () {
  44. $("#msg").text("");
  45. });
  46.  
  47. });

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

  1. private void Do()
  2. {
  3. try
  4. {
  5.  
  6. while (Enable)
  7. {
  8. Console.WriteLine("发起请求!");
  9. var url = @"http://192.168.9.6:9866/pushService.act?userId=18";
  10. using (var wc = new WebClient())
  11. {
  12. wc.Encoding = Encoding.UTF8;
  13. #region 回调处理
  14. wc.DownloadStringCompleted += (s, e) => {
  15.  
  16. if (e.Error != null)
  17. {
  18. Console.WriteLine(e.Error);
  19. }
  20. else if (e.Cancelled)
  21. {
  22. Console.WriteLine("Be Cancelled!");
  23. }
  24. else
  25. {
  26. Console.WriteLine(e.Result);
  27.  
  28. }
  29. autoReset.Set();
  30. };
  31. #endregion
  32. var uri = new Uri(url);
  33. wc.DownloadStringAsync(uri);
  34.  
  35. var isOK= autoReset.WaitOne(1000 * 60 * 5);
  36. if (!isOK)
  37. {
  38. wc.CancelAsync();
  39. }
  40.  
  41. }
  42. }
  43. }
  44. catch (Exception ex)
  45. {
  46. Console.WriteLine("错误" + ex.Message);
  47. }
  48. finally
  49. {
  50. if (Enable)
  51. {
  52. ThreadPool.QueueUserWorkItem(o => { Do(); }, null);
  53. }
  54. }
  55.  
  56. }

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个图片

asp.net 中长尾链接实现推送 -- comet的更多相关文章

  1. 在 Asp.NET MVC 中使用 SignalR 实现推送功能 [转]

    在 Asp.NET MVC 中使用 SignalR 实现推送功能 罗朝辉 ( http://blog.csdn.net/kesalin ) CC许可,转载请注明出处 一,简介 Signal 是微软支持 ...

  2. MVC 中使用 SignalR 实现推送功能

    MVC 中使用 SignalR 实现推送功能 一,简介 Signal 是微软支持的一个运行在 Dot NET 平台上的 html websocket 框架.它出现的主要目的是实现服务器主动推送(Pus ...

  3. Asp.NET MVC 使用 SignalR 实现推送功能二(Hubs 在线聊天室 获取保存用户信息)

    简单介绍 关于SignalR的简单实用 请参考 Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室) 在上一篇中,我们只是介绍了简单的消息推送,今天我们来修改一下,实现 ...

  4. AngularJS+ASP.NET MVC+SignalR实现消息推送

    原文:AngularJS+ASP.NET MVC+SignalR实现消息推送 背景 OA管理系统中,员工提交申请单,消息实时通知到相关人员及时进行审批,审批之后将结果推送给用户. 技术选择 最开始发现 ...

  5. IOS中程序如何进行推送消息(本地推送,远程推送)

    [1]-------------什么是推送消息? 我就以一张图解释------------ [2]-----------IOS程序中如何进行本地推送?----------- 2.1,先征求用户同意 1 ...

  6. JAVA使用百度链接实时推送API提交链接

    官网地址:http://data.zz.baidu.com/ 百度推广API的token获取 http://data.zz.baidu.com/site/index 填写完之后会进行验证, 验证完之后 ...

  7. Asp.NET MVC 中使用 SignalR 实现推送功能

    一,简介Signal 是微软支持的一个运行在 Dot NET 平台上的 html websocket 框架.它出现的主要目的是实现服务器主动推送(Push)消息到客户端页面,这样客户端就不必重新发送请 ...

  8. 在 Asp.NET MVC 中使用 SignalR 实现推送功能

    一,简介Signal 是微软支持的一个运行在 Dot NET 平台上的 html websocket 框架.它出现的主要目的是实现服务器主动推送(Push)消息到客户端页面,这样客户端就不必重新发送请 ...

  9. ASP.NET MVC SignalR 简单聊天推送笔记

    介绍:(抄袭于网络) ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指这样一种功能:当所连接 ...

随机推荐

  1. Yii2 基础学习

    <?php //url创建 echo Url::to(''); // same controller, different action // /index.php?r=management/d ...

  2. 在 WampServer 上手工安装 PHP 的多个版本

    手工安装新版本的PHP,只需以下步骤: 下载要安装的PHP版本.既然是用WampServer,那当然是下载Window版本的ZIP包啦:http://windows.php.net.解压到 Wamp的 ...

  3. 修改thinkpad 小红点(TrackPoint速度)

    from: http://www.jianshu.com/p/b9677e9e56ec Thinkpad大概是对Linux支持最好的笔记本了,Ubuntu大概是对硬件支持最好的Linux发行版了.Ub ...

  4. Java常见的乱码解决方式

    JAVA几种常见的编码格式(转)   简介 编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切换较多.本文将向你详细介绍 Java 中编码 ...

  5. 吴裕雄 实战PYTHON编程(9)

    import cv2 cv2.namedWindow("ShowImage1")cv2.namedWindow("ShowImage2")image1 = cv ...

  6. python网络编程——socket基础篇

    python的网络编程比c语言简单许多, 封装许多底层的实现细节, 方便程序员使用的同时, 也使程序员比较难了解一些底层的东西. 1 TCP/IP 要想理解socket,首先得熟悉一下TCP/IP协议 ...

  7. js:Date格式化

    将Date类型格式化为"yyyy/MM/dd HH:mm:ss" 函数代码如下: //Date的prototype 属性可以向对象添加属性和方法. Date.prototype.F ...

  8. 早上突然看明白 shader和材质球的关系

    计算机的世界不外乎 指令+数据 shader即Gpu指令,材质即数据

  9. jQuery html5Validate基于HTML5表单验证插件

    更新于2016-02-25 前面提到的新版目前线上已经可以访问: http://mp.gtimg.cn/old_mp/assets/js/common/ui/Validate.js demo体验狠狠地 ...

  10. swift - 代码创建 pickerView 显示或隐藏横线

    import UIKit class VC1: UIViewController { fileprivate lazy var pickerV : UIPickerView = { let v = U ...