您的位置:首页 > 编程语言 > C#

用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函数)。

在各个逻辑环节维护出错处理,保证让任务能够尽量被执行成功。

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);
        }
    }
}




最终效果









内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: