用C#搭建MSN机器人平台
2010-10-20 09:59
253 查看
由于MSN本身协议开源,于是基于其开发各种插件或者应用成为可能。
本文旨在设计&展示如何用.NET搭建一个公共的MSN平台。其基础功能包括:
1. 提供一个MSN机器人发布接口,任何用户可以使用该接口令MSN机器人向其他联系人发布信息。
2. 提供一个MSN发布接口,任何用于可以使用该接口发布自己的MSN信息。
当然该平台也可以在MSN协议允许的基础上扩大功能,这是后话。
为了使用的便捷性我们的接口使用WebService。这里对于WebService的应用不再赘述。
接下来主要讨论我们的MSN应用的技术选型及逻辑流程:
基于协议开发工作量较大,我选用了本身已经比较成熟的貌似是一个芬兰人开发的DotMSN1.2组件(现在最新版本为2.0,但是貌似比较复杂,我就没用了,有兴趣的读者可以研究一下)。其提供C#对MSN协议的基本封装。
从协议级别分析,MSN从登陆到发送消息一共有如下流程:
登陆 - 连接 - 同步 - 等待同步完成 - 创建会话 - 邀请联系人 - 等待连接成功 - 等待邀请成功 - 发送
而这中间每一步都可能异常,所以需要一个强健的错误处理的程序。在我构建的逻辑流程中主要包含以下几点:
1 . MSN连接缓存。(可以构建多个机器人实例,并且提供缓存池托管)
2 . 会话缓冲池。(在MSN实际运行过程中,对各个会话进行托管)
3 . 连接回收。(在长期没有任务的情况下,MSN机器人会自动回收,当然,这对使用者是透明的)
4 . 任务队列及多线程安全。(实际在平台运营过程中,可能是多线程并发访问的,所以对于每个连接实例提供任务队列管理,并且多线程安全)
下面看看代码:
MSN机器人类
主要思想:
每个机器人维护一个线程,该线程用于任务的轮训和执行(见Start函数)。
每个机器人维护一个任务队列(Queue<MSNTask> _tasks),一个会话缓冲池(见getConversationHandler函数)。
在各个逻辑环节维护出错处理,保证让任务能够尽量被执行成功。
MSN机器人任务类
主要思想:
任务的执行使用状态机
MSN机器人工厂类
主要思想:
维护MSN连接缓冲池
最终效果
本文旨在设计&展示如何用.NET搭建一个公共的MSN平台。其基础功能包括:
1. 提供一个MSN机器人发布接口,任何用户可以使用该接口令MSN机器人向其他联系人发布信息。
2. 提供一个MSN发布接口,任何用于可以使用该接口发布自己的MSN信息。
当然该平台也可以在MSN协议允许的基础上扩大功能,这是后话。
为了使用的便捷性我们的接口使用WebService。这里对于WebService的应用不再赘述。
接下来主要讨论我们的MSN应用的技术选型及逻辑流程:
基于协议开发工作量较大,我选用了本身已经比较成熟的貌似是一个芬兰人开发的DotMSN1.2组件(现在最新版本为2.0,但是貌似比较复杂,我就没用了,有兴趣的读者可以研究一下)。其提供C#对MSN协议的基本封装。
从协议级别分析,MSN从登陆到发送消息一共有如下流程:
登陆 - 连接 - 同步 - 等待同步完成 - 创建会话 - 邀请联系人 - 等待连接成功 - 等待邀请成功 - 发送
而这中间每一步都可能异常,所以需要一个强健的错误处理的程序。在我构建的逻辑流程中主要包含以下几点:
1 . MSN连接缓存。(可以构建多个机器人实例,并且提供缓存池托管)
2 . 会话缓冲池。(在MSN实际运行过程中,对各个会话进行托管)
3 . 连接回收。(在长期没有任务的情况下,MSN机器人会自动回收,当然,这对使用者是透明的)
4 . 任务队列及多线程安全。(实际在平台运营过程中,可能是多线程并发访问的,所以对于每个连接实例提供任务队列管理,并且多线程安全)
下面看看代码:
MSN机器人类
主要思想:
每个机器人维护一个线程,该线程用于任务的轮训和执行(见Start函数)。
每个机器人维护一个任务队列(Queue<MSNTask> _tasks),一个会话缓冲池(见getConversationHandler函数)。
在各个逻辑环节维护出错处理,保证让任务能够尽量被执行成功。
using System; using System.Collections.Generic; using System.Collections; using System.Linq; using System.Text; using System.Threading; using System.IO; using DotMSN; namespace MSNPlatform { public class MSNRobot { #region const string _user = string.Empty; string _password = string.Empty; const int CONNECT_INTEVAL = 15000; //连接重试间隔 const int COMMAND_INTEVAL = 1000; //本地命令重试间隔 const int CHECK_TASK_INTERVAL = 4000; //发送任务检测间隔 const int MAX_ERR_COUNT = 10; //最大错误次数 const int MAX_NO_TASK_COUNT = 300; //最大销毁机器人检测次数(没有任务的情况下将销毁) const int ADD_USER_WANT_TURN = 5; //添加联系人或同步等待轮数 const int ADD_USER_FAIL_COUNT = 4; //添加联系人失败次数 const int MAX_RECONNECT_TIMES = 10; //创建会话最大重试次数 const int RECONNECT_CONSATION_TURN = 8; //重新创建会话连接的次数 #endregion public MSNRobot(string user,string password) { _user = user; _password = password; Init(); } public void Init() { conversationMapping = new Hashtable(); Thread robotThread = new Thread(new ThreadStart(this.Start)); robotThread.Start(); } Messenger messenger = null; private bool _isConnected = false; //标识是否建立连接 private bool _isSynchronized = false;//是否同步 private bool Connect() { try { Logger.Log("连接服务器中.."); messenger = new Messenger();//实例化MSN类 messenger.ConversationCreated += new Messenger.ConversationCreatedHandler(ConversationCreated); messenger.SynchronizationCompleted += new Messenger.SynchronizationCompletedHandler(OnSynchronizationCompleted);//同步用户列表回调 messenger.Connect(_user, _password);//连接服务器(阻塞) Logger.Log("OK"); messenger.SynchronizeList();//开始同步 return true; } catch (Exception e) { Logger.Log("连接异常 " + e.ToString()); return false; } } private void ConnectAndSynchronize() { conversationMapping.Clear(); //清空会话映射 while (this.Connect() == false) //连接服务器 { Thread.Sleep(CONNECT_INTEVAL); } } private void ConversationCreated(Messenger sender, ConversationEventArgs e)//连接成功回调 { Logger.Log("会话建立完成"); _isConnected = true; e.Conversation.MessageReceived += new Conversation.MessageReceivedHandler(MessageReceived); } //收到联系人消息时处理函数 private void MessageReceived(Conversation sender, MessageEventArgs e) { string msg = e.Message.Text; Logger.Log(e.Sender.Name + "说:" + msg); sender.SendMessage("不要向我发消息,没任何意义"); } private void OnSynchronizationCompleted(Messenger sender, EventArgs e) { Logger.Log("同步完成"); messenger.SetStatus(MSNStatus.Online); _isSynchronized = true; } private Hashtable conversationMapping = null; private int errCount = 0; private Conversation getConversationHandler(string addr,bool isForceReconnect) { try { if ((Conversation)(conversationMapping[addr]) == null || isForceReconnect) { conversationMapping[addr] = messenger.RequestConversation(addr); } return (Conversation)(conversationMapping[addr]); } catch (Exception e) { Logger.Log("会话创建异常 " + e.ToString()); return null; } } /// <summary> /// MSN工作者主流程 /// </summary> private void Start() { bool reconnectFlag = true;//重联服务器标志 int noTask = 0;//没有任务统计 while (true) //发送任务 { if (reconnectFlag) //重新连接服务器 { errCount = 0; reconnectFlag = false; ConnectAndSynchronize(); } Thread.Sleep(CHECK_TASK_INTERVAL); //等待间隔 lock (this) { try { if (_tasks.Count > 0) //任务队列不为空 { noTask = 0; //清空没有任务标记 MSNTask task = _tasks.Dequeue(); //取一个任务 Conversation con = this.getConversationHandler(task.Addr, false); //建立会话 Logger.Log("开始任务 联系人=" + task.Addr + " , 信息=" + task.Msg); if (con != null && con.Connected) //若会话已经连接上 { if (task.Status == MSNTaskStatus.Ready)//是准备任务 { if (con.Invited)//如果已邀进会话 { task.Status = MSNTaskStatus.Doing; Logger.Log(task.Addr + "邀请成功,发送"); con.SendMessage(task.Msg); //发送消息 } else //否则邀请进行会话(有可能 1.该用户不是联系人 2.会话尚未建立成功) { task.Status = MSNTaskStatus.Adding; Logger.Log(task.Addr + " 邀请中.."); messenger.AddContact(task.Addr); //添加为联系人 if (++task.AddTurn < ADD_USER_FAIL_COUNT) //若小于添加联系人超限次数 _tasks.Enqueue(task); //将任务放回到尾部 else Logger.Log("添加联系人 超时,丢弃任务"); task.WaitTurn = ADD_USER_WANT_TURN; messenger.SynchronizeList();//开始同步 } } else if (task.Status == MSNTaskStatus.Adding)//正在添加联系人或等待同步 { _tasks.Enqueue(task); //将任务放回到尾部 if (--task.WaitTurn <= 0) task.Status = MSNTaskStatus.Ready; } errCount = 0;//有一次成功,则清零错误次数 } else if (con == null) { Logger.Log("任务失败"); task.Status = MSNTaskStatus.Fail; } if (!con.Connected) //尚未连接 { Logger.Log("未连接,等待连接.."); if (task.Reconnect != 0 && task.Reconnect % RECONNECT_CONSATION_TURN == 0) { Logger.Log("建立会话连接超时,重新建立"); this.getConversationHandler(task.Addr, true); } if (++task.Reconnect <= MAX_RECONNECT_TIMES) { _tasks.Enqueue(task); } else { errCount++; Logger.Log("建立会话连接失败次数超限,放弃任务"); } } } else { noTask++; if (noTask > MAX_NO_TASK_COUNT) //销毁本机器人 { Logger.Log("长期没有任务,暂时销毁机器人"); MSNRobotFactory.unRegist(_user); break; } } } catch (Exception e) { errCount++;//错误次数累加 Logger.Log("异常 " + e.ToString()); } finally { if (errCount > MAX_ERR_COUNT) //错误累计满,重新连接服务器 { Logger.Log("错误累计满,重新连接服务器"); reconnectFlag = true; } } } } } Queue<MSNTask> _tasks = new Queue<MSNTask>(); /// <summary> /// 增加发送任务 /// </summary> /// <param name="addr">发送到email地址</param> /// <param name="msg">发送内容</param> public int AddTask(string addr, string msg) { return this.AddTask(new MSNTask(addr, msg)); } /// <summary> /// 增加任务 /// </summary> /// <param name="task"></param> public int AddTask(MSNTask task) { lock (this) { Logger.Log("添加任务 addr=" + task.Addr + ",msg=" + task.Msg); _tasks.Enqueue(task); } return task.ID; } } }
MSN机器人任务类
主要思想:
任务的执行使用状态机
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MSNPlatform { public enum MSNTaskStatus { Ready, Doing, Fail, Success, Adding, Connecting } public class MSNTask { public MSNTask(string addr, string msg) { Addr = addr; Msg = msg; Status = MSNTaskStatus.Ready; this.ID = idCount++; AddTurn = 0; WaitTurn = 0; Reconnect = 0; } private static int idCount = 1; //任务id计数器 internal string Addr { get; set; } internal string Msg { get; set; } internal MSNTaskStatus Status { get; set; } public int ID { get; set; } public int WaitTurn { get; set; } public int AddTurn { get; set; } public int Reconnect { get; set; } } }
MSN机器人工厂类
主要思想:
维护MSN连接缓冲池
using System; using System.Data; using System.Configuration; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq; using System.Collections; namespace MSNPlatform { public class MSNRobotFactory { static private Hashtable userHandlerMapping = new Hashtable(); static public MSNRobot getRobot(string user,string pw) { if (userHandlerMapping[user] == null) { userHandlerMapping[user] = new MSNRobot(user, pw); } return (MSNRobot)(userHandlerMapping[user]); } static public void unRegist(string user) { userHandlerMapping.Remove(user); } } }
最终效果
相关文章推荐
- 【原创】C#搭建足球赛事资料库与预测平台(5) 赔率数据表设计1
- 【原创】C#搭建足球赛事资料库与预测平台(2) 数据库与XCode组件
- C#设计模式之微信H5牛牛源码平台搭建适配器模式
- Activemq 平台搭建与C#示列
- 【原创】C#搭建足球赛事资料库与预测平台(6) 赔率数据表设计2
- 【原创】C#搭建足球赛事资料库与预测平台(3) 基础数据表设计
- C#搭建足球赛事资料库与预测平台(1) 基本介绍
- 【原创】C#搭建足球赛事资料库与预测平台(6) 赔率数据表设计2
- 【原创】C#搭建足球赛事资料库与预测平台(2) 数据库与XCode组件
- 在windows平台使用Apache James搭建邮件服务器以及使用C#向外网发送邮件
- 【原创】C#搭建足球赛事资料库与预测平台(3) 基础数据表设计
- 【原创】C#搭建足球赛事资料库与预测平台(4) 比赛信息数据表设计
- 【原创】C#搭建足球赛事资料库与预测平台(4) 比赛信息数据表设计
- C#搭建足球赛事资料库与预测平台(1) 基本介绍
- 【原创】C#搭建足球赛事资料库与预测平台(5) 赔率数据表设计1
- 搭建Python+PyQt+Eric平台进行图形化开发
- USB--平台搭建--4--在Visual Studio 2005里编译驱动
- ubuntu环境下搭建操作系统实验平台(Virtualbox和Bochs)
- 【集】使用Red5和FFMpeg搭建在线Flash流媒体分享平台
- Windows平台下搭建Git服务器的图文教程