基于SuperSocket的IIS主动推送消息给android客户端

      在上一篇文章《基于mina框架的GPS设备与服务器之间的交互》中,提到之前一直使用superwebsocket框架做为IIS和APP通信的媒介,经常出现无法通信的问题,必须一天几次的手动回收程序池,甚至重起服务器,通常周末接到一个陌生电话,就是说客户端无法登录了,说多了都是泪。痛定思痛,开始找解决方案,其实superwebsocket以IIS做为宿主,就注定他可能不稳定了,当然,它部署非常方便;为了稳定,我开始尝试使用SuperSocket,当然,这也注定了后期部署会麻烦些;生活就是这样哈,鱼和熊掌难兼得。学习一个新东西,就如同一个打怪升级做任务的历程,其中有数不清的陷阱,当然也有绚丽景色。关于服务,WCF等几乎都是第一次运用,其中肯定有很多不对的地方,还请了解的朋友们指出来,以免误了别人。对于SuperSocket之前也只是听说过,本次也只是简单的应用,如有应用不对,或者说得不对的地方,还请江大渔同学指出。另外,江大牛做的事让我的开发变得简单了,在此,对其表示由衷的感谢和敬佩!

消息传递流程

  消息传递流程如图1所示,创建一个Windows Service,并启动superSocket,发布一个WCF,以Windows Service做为宿主,随服务启动与关闭。 IIS通过WCF传递消息给Windows Service,然后再传给superSocket,再传递给android客户端;客户端上传坐标处理给superSocket,再保存于数据库。

                (图1)

 

 

SuperSocket

  以下内容是摘自其官网,大家可以自行查看:SuperSocket 是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架。你无须了解如何使用 Socket, 如何维护 Socket 连接和 Socket 如何工作,但是你却可以使用 SuperSocket 很容易的开发出一款 Socket 服务器端软件,例如游戏服务器,GPS 服务器, 工业控制服务和数据采集服务器等等。-- http://www.supersocket.net/

 

实现自己的AppSession,AppServer

   下载最新版源码,目前最新版应该是1.6.3,好像是马上要发布1.6.4了吧。解决方案如图2,目前我只是简单的应用,源码就没细看了,其实也看不懂,哈哈。

    

          (图2)

      其文档中如下描述:

         AppSession 代表一个和客户端的逻辑连接,基于连接的操作应该定于在该类之中。你可以用该类的实例发送数据到客户端,接收客户端发送的数据或者关闭连接。

     AppServer 代表了监听客户端连接,承载TCP连接的服务器实例。理想情况下,我们可以通过AppServer实例获取任何你想要的客户端连接,服务器级别的操作和逻辑应该定义在此类之中。

   所以,通常情况要根据自己的业务来实现自己的AppSession,AppServer。如,我需求在session断开时,修改app状态;或者我的AppSession有自己特殊的属性。
下面是我实现的自己的AppSession(NoticeSession),AppServer(NoticeServer),有兴趣可以瞥下。

NoticeSession代码如下:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;using SuperSocket.Common;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Protocol;
using System.Threading;
using Hangjing.SQLServerDAL.serverinterface;namespace SuperSocket.SocketService
{public class MESSAGETYPE{/// <summary>/// 1表示消息/// </summary>public const uint MSG = 1;/// <summary>/// 0表示订单/// </summary>public const uint ORDER = 0;}/// <summary>/// 自定义连接类MySession,继承AppSession,并传入到AppSession  /// </summary>public class NoticeSession : AppSession<NoticeSession>{bool isSendMessage = false;public StringDictionary Cookies { get; private set; }/// <summary>/// 数据编号,配送员,或者商家编号等/// </summary>public int DataID{get;set;}/// <summary>/// 类型:1表示骑士,2表示商家/// </summary>public int Type{set;get;}/// <summary>/// 用户名;/// </summary>public String UserName{get;set;}/// <summary>/// 密码/// </summary>public String Password{get;set;}protected override void OnSessionStarted(){}protected override void HandleUnknownRequest(StringRequestInfo requestInfo){//Logger.Debug("NoticeSession.OnSessionStarted:Unknow request");
        }protected override void HandleException(Exception e){//Logger.Debug("NoticeSession.OnSessionStarted:Unknow request");
        }protected override void OnSessionClosed(CloseReason reason){Logout();base.OnSessionClosed(reason);}/// <summary>/// 根据登录的参数,保存cookie ,并设置属性/// </summary>public void SetCookie(string cookieValue){var cookies = new StringDictionary();if (!string.IsNullOrEmpty(cookieValue)){string[] pairs = cookieValue.Split(';');int pos;string key, value;foreach (var p in pairs){pos = p.IndexOf('=');if (pos > 0){key = p.Substring(0, pos).Trim();pos += 1;if (pos < p.Length)value = p.Substring(pos).Trim();elsevalue = string.Empty;cookies[key] = Uri.UnescapeDataString(value);}}}this.Cookies = cookies;this.UserName = Cookies["name"];this.Password = Cookies["password"];this.Type = Convert.ToInt32(Cookies["type"]);}/// <summary>/// 向客户端发送消息(0 表示订单 ,1表示消息)/// </summary>/// <param name="type">(0 表示订单 ,1表示消息)</param>/// <param name="message">消息内容(json)</param>public void SendMessage(uint type, String message){while (isSendMessage){Thread.Sleep(1);}isSendMessage = true;String value = "";switch (type){case MESSAGETYPE.ORDER:value = "ORDER::" + message;break;case MESSAGETYPE.MSG:value = "MSG::" + message;break;}this.Send(value);isSendMessage = false;}/// <summary>/// session退出,对应骑士下线/// </summary>public void Logout(){if (DataID != 0 && Type == 1){APPUser user = new APPUser(this.UserName, this.Password, this.SessionID, this.Type);if (user.app != null){user.app.UpdateLoginState(this.SessionID, 0);}}}/// <summary>/// 根据编号为类型获取session/// </summary>/// <param name="id"></param>/// <param name="type"></param>/// <returns></returns>public NoticeSession GetSession(int id, int type){NoticeSession session = this.AppServer.GetAllSessions().Where(a => a.DataID == id && a.Type == type).FirstOrDefault();if (session != null){return session;}else{return null;}}}
}
View Code

 

NoticeServer代码如下:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using SuperSocket.SocketBase;
 6 using SuperSocket.SocketBase.Config;
 7 using SuperSocket.SocketBase.Protocol;
 8 using Hangjing.SQLServerDAL.serverinterface;
 9 
10 namespace SuperSocket.SocketService
11 {
12     /// <summary>
13     /// 自定义服务器类MyServer,继承AppServer,并传入自定义连接类MySession 
14     /// </summary>
15     public class NoticeServer : AppServer<NoticeSession>
16     {
17         protected override bool Setup(IRootConfig rootConfig, IServerConfig config)
18         {
19             return base.Setup(rootConfig, config);
20         }
21 
22         protected override void OnStarted()
23         {
24             base.OnStarted();
25         }
26 
27         protected override void OnStopped()
28         {
29             base.OnStopped();
30         }
31 
32         /// <summary>  
33         /// 输出新连接信息  
34         /// </summary>  
35         /// <param name="session"></param>  
36         protected override void OnNewSessionConnected(NoticeSession session)
37         {
38             base.OnNewSessionConnected(session);
39             //输出客户端IP地址  
40             //session.Logger.Debug("\r\n NoticeServer.OnNewSessionConnected->" + session.LocalEndPoint.Address.ToString() + ":连接");
41 
42         }
43 
44         /// <summary>  
45         /// 输出断开连接信息  
46         /// </summary>  
47         /// <param name="session"></param>  
48         /// <param name="reason"></param>  
49         protected override void OnSessionClosed(NoticeSession session, CloseReason reason)
50         {
51             //输出客户端IP地址</span>  
52             //session.Logger.Debug("\r\n NoticeServer.OnSessionClosed->" + session.LocalEndPoint.Address.ToString() + ":断开 dataid=" + session.DataID + "&Type=" + session.Type);
53             //退出
54             if (session.DataID != 0)
55             {
56                 APPUser user = new APPUser(session.UserName, session.Password, session.SessionID, session.Type);
57                 if (user.app != null)
58                 {
59                     user.app.UpdateLoginState(session.SessionID, 0);
60                 }
61             }
62             base.OnSessionClosed(session, reason);
63         }
64 
65     }
66 }
View Code

 

实现自己的消息处理机制

   消息都会进到MainService.NewRequestReceived 方法中,所以我在这里处理自己的消息。默认消息机制里,会把消息序列化为 StringRequestInfo,这个对像包含Key和Body,默认是用空格分隔的。我主要实现app登录(建立链接),和app上传坐标等两个消息,NewRequestReceived 方法代码如下

  

/// <summary>/// 收到新的消息/// </summary>/// <param name="session"></param>/// <param name="requestInfo"></param>void NewRequestReceived(NoticeSession session, StringRequestInfo requestInfo){//session.Logger.Debug("Key=" + requestInfo.Key + "|body=" + requestInfo.Body);switch (requestInfo.Key){case "Cookie:"://这里为了兼容原来的app登录发送的数据
                    {session.SetCookie(requestInfo.Body);User user = new User(session);Thread thdProcess = new Thread(user.LoginThread);thdProcess.Start();}break;case "GPS":{string json = requestInfo.Body;if (session.DataID == 0 && json == ""){return;}User user = new User(session,json);Thread thdProcess = new Thread(user.UploadGPS);thdProcess.Start();}break;}}
View Code

 

 LoginThread 主要实现验证用户名,密码后,返回用户相关信息,具体代码如下:

 /// <summary>/// 登录函数/// </summary>public void LoginThread(){String state = "";String message = "";APPUser user = new APPUser(session.UserName, session.Password, session.SessionID, session.Type);if (user.app == null){session.Logger.Debug("登录:" + session.UserName + " type=" + session.Type+"  对像为空");return;}int userid = user.app.APPLogin(session.UserName, session.Password, session.SessionID);if (userid > 0){NoticeSession ol = session.GetSession(userid, session.Type);if (ol != null){state = "-2";message = "Login::{\"userid\":\"" + session.DataID.ToString() + "\",\"state\":\"" + state + "\"}";ol.Send(message);Thread.Sleep(2);ol.Close();}session.DataID = userid;state = "1";message = user.app.getLoginJSON(userid,state);message = Utils.ToUTF8(message);session.Send(message);return;}else{state = "-1";message = "Login::{\"userid\":\"" + session.DataID.ToString() + "\",\"state\":\"" + state + "\"}";}session.Send(message);Thread.Sleep(2);session.Close();}
View Code

  考虑到可能会有骑士,商家,取餐员等对像同时存在,为了保证服务程序的通用性,抽象出每个对像的相同操作。面向接口进行编程,如下图

 

 

  经过,以上简单步骤,运行InstallService.bat,即可创建服务,监听指定端口了。可用TCP&UDP测试工具,简单测试下,看效果,如下图:

  

  

  android客户端方面,是我同事基于mina实现的,这里我就不介绍了,其实我也不太懂,我只是简单的把他原来以websocket协议实现的,修改成了纯数据的了。

创建WCF服务库

  当时在考虑如果把消息(如把订单调度给某个配送员了)传给Windows Service时,考虑了多个方法:想过用数据库,想过用消息队列;但是都觉得不太好,当WCF飘过脑海时,一下子觉得这个可行,其实在此之前,我也只是听过说而已,也许就是因为不熟悉,觉得神奇,才让我觉得稀奇吧。说干就干,看了几篇文章,实现了一个简单的WCF。UserNoticeService.cs实现代码如下,只有一个简单的方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;namespace Hangjing.WCFService
{[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]public class UserNoticeService : IUserNoticeService{/// <summary>/// 添加消息/// </summary>/// <param name="userid">用户编号</param>/// <param name="usertype">用户类型 1表示骑士,2表示商家</param>/// <param name="messagetype">消息类型 消息类型:0表示订单,1表示纯消息。</param>/// <param name="message">消息json</param>public void AddMessage(int userid, int usertype, int messagetype, string message){NoticeInfo model = new NoticeInfo();model.UserId = userid;model.UserType = usertype;model.MessageType = messagetype;model.Message = message;NoticeManager nm = NoticeManager.GetInstance();nm.Add(model);}}
}
View Code

 

使用委托及时传递消息

  当UserNoticeService.AddMessage 接收到消息后,如何传递给 Windows Service时,也纠结了好久,直到就快放弃思考,准备用消息队列来实现时,才想到委托。这个东西吧,一直觉得很多神奇,之前也花了很多时间去理解,一直觉得似懂非懂的感觉,原来是没有真正的应用。代码部分就比较简单了,以下是NoticeManager.cs相关代码,在UserNoticeService.AddMessage中执行添加的方法。

   

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;namespace Hangjing.WCFService
{/// <summary>/// 对消息的管理/// </summary>public class NoticeManager{public static List<NoticeInfo> NoticeList = new List<NoticeInfo>();public static object m_SessionSyncRoot = new object();public event AddHandler AddEvent = null;private static NoticeManager instance;static NoticeManager()  //类型构造器,确保线程安全
        {instance = new NoticeManager();}private NoticeManager() //构造方法为private,这就堵死了外界利用new创建此类型实例的可能
        {Thread.Sleep(50);//此处模拟创建对象耗时
        }public static NoticeManager GetInstance() //次方法是获得本类实例的唯一全局访问点
        {return instance;}/// <summary>/// 添加方法/// </summary>/// <param name="notice"></param>public void Add(NoticeInfo model){//后期再考虑消息的存储//foreach (var item in NoticeManager.NoticeList)//{//    if (item.UserId == model.UserId && item.UserType == model.UserType)//    {//        lock (NoticeManager.m_SessionSyncRoot)//        {//            NoticeManager.NoticeList.Remove(item);//        }//    }//}//lock (NoticeManager.m_SessionSyncRoot)//{//    NoticeManager.NoticeList.Add(model);//}if (this.AddEvent != null){this.AddEvent(model);}}}public delegate void AddHandler(NoticeInfo notice);}
View Code

 

在MainService中注册委托

    NoticeManager nm = NoticeManager.GetInstance();
    nm.AddEvent += nm_AddEvent;

IIS通过WCF发送消息

  网站中引用WCF,比较方便,VS 中网站右键,添加-》服务引用,如下图,

  

    调用也非常简单,两行代码:

        wcfnotice.UserNoticeServiceClient unsc = new wcfnotice.UserNoticeServiceClient();
        ///发订单
        unsc.AddMessage(id, se, type, msg);

 

感谢

  这篇文章,写到一半时,特别纠结,觉得自己做的事件,好像没有什么技术含量,只是基于superSocket框架,做了简单的应用,一度想放弃这篇文章,但转念一想,我用这个程序替换原来的 SuperWebSocket后,确实稳定了,app任何时间都可以登录了,也许能对那些正在和我们一样用SuperWebSocket的有所帮助,也希望能共同交流。当然,还有一个原因让我坚持写完了,那就是对江大牛的感谢和敬佩,也希望他能继续完善这个框架。

  

      成为一名优秀的程序员!

转载于:https://www.cnblogs.com/jijunjian/p/4049203.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/459991.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

明细表如何添加重量_关于Revit中明细表标准的导出及导入

Revit中明细表的作用非常大&#xff0c;项目中的数据归类整理及统计都离不开它&#xff0c;今天给大家分享一下如何在Revit中进行明细表标准的导出及导入&#xff0c;减少在实际项目中的重复性工作。1. 首先在Revit中新建一个项目文件&#xff0c;在平面视图中随便画几条管道&a…

鸿蒙还是不是安卓,华为捐赠鸿蒙核心架构!是否形成“三足鼎立”?

发布会已经开完&#xff0c;万物互联时代也已开启。经过测试&#xff0c;鸿蒙系统支持几乎所有的安卓软件&#xff0c;换句话说安卓用户可以无缝过渡到鸿蒙系统&#xff0c;相比安卓&#xff0c;速度更快&#xff0c;耗电量更低&#xff0c;这无疑比安卓系统的体验好出一个档次…

Filter基金会

一个、总结 简单的说&#xff0c;Filter的作用就是拦截(Tomcat的)service&#xff08;Request&#xff0c;Response&#xff09;方法。拿到Request、Response对象进行处理。然后释放控制。继续自己主动流转。其运用的还是“分层”的思想。至于为什么要增加这一层&#xff0c;为…

python2处理耗时任务_RabbitMQ Go客户端教程2——任务队列/工作队列

本文翻译自RabbitMQ官网的Go语言客户端系列教程&#xff0c;本文首发于我的个人博客&#xff1a;liwenzhou.com&#xff0c;教程共分为六篇&#xff0c;本文是第二篇——任务队列。这些教程涵盖了使用RabbitMQ创建消息传递应用程序的基础知识。 你需要安装RabbitMQ服务器才能完…

使用 CSS3 伪元素实现立体的照片堆叠效

CSS3 里引入的伪元素让 Web 开发人员能够在不需要额外添加 HTML 标签的情况下制作出复杂的视觉效果。例如&#xff0c;:before 和 :after 这个两个 CSS3 伪元素就可以帮助你实现很多有趣的效果。本教程将告诉你如何使用 CSS3 为元素创建一组漂亮的图片堆叠效果。 效果演示 …

启发式搜索A*算法

A* 寻路算法 (2011-02-15 10:53:11) 转载▼标签&#xff1a; 游戏 分类&#xff1a; 算法概述 虽然掌握了 A* 算法的人认为它容易&#xff0c;但是对于初学者来说&#xff0c; A* 算法还是很复杂的。 搜索区域(The Search Area) 我们假设某人要从 A 点移动到 B 点&#xff0c…

centos7全盘备份到本地_CentOS7下制作openssl1.1.1i RPM包并升级

点击上方"walkingcloud"关注&#xff0c;并选择"星标"公众号CentOS7下制作openssl1.1.1i RPM包并升级OpenSSL最新漏洞 OpenSSL官方发布了拒绝服务漏洞风险通告&#xff0c;漏洞编号为CVE-2020-1971漏洞详情OpenSSL是一个开放源代码的软件库包&#xff0c;应…

计算机网络基础专业找工作,2021计算机网络技术前景怎么样? 好找工作吗

很多同学想知道计算机网络技术前景怎么样&#xff0c;以下是一些相关信息的整理&#xff0c;希望能对同学们有所帮助&#xff01;计算机网络技术前景从目前的情况看&#xff0c;企业的IT技术管理岗位一般设置为企业信息主管、总监等&#xff1b;工程技术岗位设置为网络工程师、…

安装 VMware Tools

第一步&#xff1a;挂载VMware Tools 第二步&#xff1a; 将上图VMware Tools-9.2.0 XXXX.tar.gz 复制到tmp目录&#xff08;其他目录也行&#xff0c;没有权限操作需要 chmod 777 XXX 修改来获取权限&#xff09; 第三步&#xff1a;解压 tar xvzf VMwareTools-9.2.0-799703.t…

LINUX添加一块网卡地址配置及问题

如何如何网卡服务重启慢关掉service NetworkManager stopchkconfig NetworkManager off 关于NetworkManager外链&#xff1a;http://www.linuxidc.com/Linux/2013-08/88809.htm 查看cat /etc/udev/rules.d/70-persistent-net.rules是否网卡名有重复VMware虚拟机安装好CentOS6.…

[每日一题jQuery] jQuery选择器总结:进一步过滤、同级操作、后代操作

jQuery选择器继承自CSS的风格&#xff0c;可以通过jQuery选择器找出特定的DOM元素&#xff0c;在此基础上对该元素做相应处理。jQuery不仅支持简单的标签选择器、类选择器、id选择器&#xff0c;还针对表单状态、子元素、元素顺序提供相应的选择器。如果熟练运用&#xff0c;则…

计算机桌面备份在哪里,电脑备份文件在哪里

电脑是我们经常使用的工具&#xff0c;为了放在电脑出现系统问题&#xff0c;有的朋友会将电脑系统备份&#xff0c;但是不知道电脑备份文件放在什么地方了&#xff0c;电脑备份文件在哪里呢&#xff1f;很多朋友还是不知道的&#xff0c;所以针对电脑备份文件保存在哪里的问题…

如何用python打印田字格_如何用 3D 打印一双顶级跑鞋回形针

马拉松赛场上的跑鞋五光十色&#xff0c;除了美观之外&#xff0c;它们还有一个重要作用&#xff1a;保护运动员不受伤害。跑步时&#xff0c;从脚碰到地面开始会受到地面的反作用力。从着陆的减速到随着脚步推蹬过程的加速&#xff0c;这一过程中脚和地面的压力会达到两个峰值…

什么可以作为gcroot_面包果既能当水果又可以作为粮食,国内却无法普及,这是为什么?...

水果是我们大多数人日常都会吃的一种食物&#xff0c;现在国内的水果种类也是非常多&#xff0c;不管是国内本有的还是从国外进口的。但是你吃过面包果吗&#xff1f;可千万不要把它和非洲大陆上的面包树混为一谈&#xff0c;面包果和它半毛钱关系没有&#xff0c;它的原产地在…

cookies的存值问题

2019独角兽企业重金招聘Python工程师标准>>> cookies存值问题&#xff0c;项目中遇到问题初始化时由于cookies中存在冒号导致存到cookies中时取出来被转码了&#xff0c;冒号的转码从cookies中取出来为%A3 &#xff0c;当然一开始我的设想是保存到cookies换一种保存…

c++ 一个函数包括多个返回值判断_Python函数的概念和使用

函数为了便于程序的维护和更好的实现模块化&#xff0c;好的程序都会分解为很多函数。可以这么说&#xff0c;对于任何的编程语言&#xff0c;函数都是一个非常重要的概念。python 不仅简化了函数的定义过程&#xff0c;而且还大量借鉴了其他函数编程语言中的优秀特性。本章内容…

微型计算机中JNZ,微机原理jnz是什么指令_微机原理内存分配图

微机原理 数据传送指令微机原理 4指令系统 3通过数据传输地址 地址传送指令 标志传送指令微机原理 4指令系统 3通过数据传输地址 地址传送指令 标志传送指令微机原理指令 微机原理sub指令 csdn微机原理 4指令系统 3通过数据传输地址 地址传送指令 标志传送指令微机原理 4指令系…

分段处理_连续油管无限级可开关固井滑套分段压裂工艺

连续油管无限级可开关固井滑套分压工艺具有无需射孔、处理级数不受限制、施工效率高等特点&#xff0c;同时可为后期水平井控水及重复压裂提供井筒条件。作业前滑套与套管管柱一趟下入井内&#xff0c;正常进行固井后&#xff0c;依靠固井水泥实现压裂层间封隔。通过连续油管下…

access 根据id删除数据_小程序云开发之数据库自动备份丨云开发101

钻石有价&#xff0c;数据无价。我们通常会把重要的业务数据存放在数据库中&#xff0c;并需要对数据库做定时的自动备份工作&#xff0c;防止数据异常丢失&#xff0c;造成无法挽回的损失。小程序云开发提供了方便的云数据库供我们直接使用&#xff0c;云开发使用了腾讯云提供…

国家职业资格计算机调试维修技师试题,电工国家职业资格三级(高级)理论试题...

电工国家职业资格三级理论试题一、单选题(第1题&#xff5e;第60题。选择一个正确的答案&#xff0c;将相应的字母填入题内的括号中。每题1.0分&#xff0c;满分60分。)1.异步测速发电机的空心杯转子是用( )材料做成的。(A)低电阻 (B)高电阻 (C)低导磁 (D…