MSNPSharp发送消息过程详解

来源:互联网 发布:侧边栏展开收缩js特效 编辑:程序博客网 时间:2024/04/29 19:49

作者: 吴滂

本文欢迎转载,转载时请标明出处.

MSNPSharp项目地址: http://code.google.com/p/msnp-sharp

源代码svn地址: http://msnp-sharp.googlecode.com/svn/

    我们直入正题。MSNPSharp的消息发送机制的确和大部分人想的不太一样,一般人概念上发送消息就一个步骤:调用自己写的 SendXXXX 函数(该函数实现向某个用户发送网络数据)就可以直接实现向对方通信了。其实这样做要有个前提,那就是你的SendXXX函数(下文简称Send)在被调用的时候,要么它能同步地(是的,同步很重要)找到你要的目标用户,并且立即建立连接发送数据,要么它就把和对方握手、缓冲向目标用户建立连接成功之前输入数据这些功能封装了,以至于调用者根本看不见这些步骤。然而,遗憾的是,为了能让程序员更灵活地控制发送消息的各个步骤,MSNPSharp的SBMessageHandler.SendtextMessage函数设计不符合以上所有假设。换言之,你必须自己处理连接中的所有过程。

    很多人看到这里可能会觉得这样做实在太麻烦,其实不然,只要你了解了MSN向对方发送消息的整个过程就会觉得很简单了。

    要使用MSNPSharp向你的好友发送消息,首先必须建立一个Conversation对象,这个对象可以通过调用Messenger类的CreateConversation函数获得。

    建立了Conversation就可以通话了么?答案是否定的,因为这个Conversation这时候只有你一个人,就好比你要向一个朋友通电话,你直接拿起话筒就能和对方说话么?

    于是下一步我们该干什么呢?让我们回到通电话的那个例子上来。我们拿起话筒,理所当然做的第二件事情就是——拨号。MSNPSharp也一样,你得“拨号”把对方邀请到你的Conversation里面来,这很简单,调用你先前得到的那个Conversation对象的Invite函数,那个函数只有一个参数,就是目标好友的Windows Live 帐号(前提是他没有Block你),或者干脆就是你目标好友的Contact对象。

    完事了么?很明显没有。你拨号了之后还得等人接了才能通话。当然在现实生活中完成这个动作很简单:你只需要拿着话筒等待就可以了,因为拨号的这个过程是线性的(或者说是同步的),动作一个接一个地发生。但是写程序就没有那么走运了,要知道Invite对方直到对方响应可能是一个很长的过程,如果是同步的话,那么可能需要等待很长的时间,以至于你不得不建立多条线程来处理每一个Invite。所以MSNPSharp采用的是异步+事件通知的设计:Invite后Invite函数立即返回,当对方“接听”后,会在Conversation.SwitchBrard触发一个事件:ContactJoined ,这个时候你才可以用 Conversation.SwitchBoard.SendTextMessage向对方发送消息。

    通常很多人在ContactJoined事件之前就向对方调用SendTextMessage,那结果只有一个:发送失败。如果在ContactJoined事件之前UI又有几条消息要被指定发送,那么建议先用个队列或数组之类保存这些消息,等ContactJoined事件触发了之后再发送。

    事情看上去大功告成了。但是不得不告诉各位一个坏消息,提供通话服务的MSN“电信局”很吝啬,如果你俩通话过程中超过30秒到一分钟一言不发,它会把你们的通话掐断(事实上应该是如果对方使用的是官方的MSN客户端,对方主动退出,据测试)。这个时候 Conversation.SwitchBard 的 ContactLeft 事件会被触发,通知你对方用户退出Conversation,Conversation.SwitchBoard.Contacts.Count属性会自减1,表示在此Conversation里面的Contact少了一个。(如果一个Conversation里面所有对方用户都退出了,会触发AllContactsLeft事件,这时可以直接调用Conversation.Switchboard.Close()关闭Conversation)

    这时问题就来了,如果我想向一个因为长时间不发送消息而退出的用户重新发送消息该怎么办呢?恩,是的,重新调用Messanger. CreateConversation再建立一个Conversation Invite他们一次。但是请记住就是,在他们Left之前把退出的用户记录下来,要不然你不知道该重新邀请谁,还有,千万别忘记,在ContactJoin之前把要发送的消息缓冲,join之后才能发送。

    最后加一句,所有的示例代码都可以在MSNPSharp的那个Example客户端看到。

 

//本代码取自MSNPSharp Example Client ConversationForm.cs        private void Switchboard_ContactJoined(object sender, ContactEventArgs e)        {            if (conversationTextBox.InvokeRequired)            {                Delegate_Switchboard_ContactJoined d = new Delegate_Switchboard_ContactJoined(Switchboard_ContactJoined);                object[] args ={ 0, 0 };                args[0] = sender;                args[1] = e;                conversationTextBox.Invoke(d, args);   //线程切换            }            else            {                conversationTextBox.Text += "* " + e.Contact.Name + " joined the conversation/r/n";                _contactStatus[e.Contact.Mail.ToLowerInvariant()] = true;                //Send all messages and nudges                if (_messagequene.Count > 0)                {                    for (int i = 0; i < _messagequene.Count; i++)                    {                        _conversation.Switchboard.SendTextMessage(_messagequene[i]); //把之前缓冲的消息发送出去                    }                    _messagequene.Clear();                }                if (_nudgequene.Count > 0)                {                    foreach (object ob in _nudgequene)                    {                        _conversation.Switchboard.SendNudge();  //把之前缓冲的闪屏发送出去                    }                    _nudgequene.Clear();                }            }        }        private void Switchboard_ContactLeft(object sender, ContactEventArgs e)        {            if (conversationTextBox.InvokeRequired)            {                conversationTextBox.Invoke(new ContactChangedEventHandler(Switchboard_ContactLeft),                    new object[] { sender, e });            }            else            {                conversationTextBox.Text += "* " + e.Contact.Name + " left the conversation/r/n";                if (!_leftusers.Contains(e.Contact.Mail))                    _leftusers.Add(e.Contact.Mail);          //记录下离开的用户                _contactStatus[e.Contact.Mail.ToLowerInvariant()] = false;            }        }        void Switchboard_AllContactsLeft(object sender, EventArgs e)        {            RemoveEvent();            Conversation.Switchboard.Close();  //所有的用户都离开了,关闭Conversation.        }
         //发送消息         private void SendInput()         {           // check whether there is input           if(inputTextBox.Text.Length == 0) return;  //不能发送空消息            TextMessage message = new TextMessage(inputTextBox.Text);            _typingMessageSended = false;            inputTextBox.Text = String.Empty;            conversationTextBox.Text += "You say: " + message.Text + "/r/n";            //All contacts left, recreate the conversation            if (ReInvite())  //如果对方已经退出会话,重新邀请            {                _messagequene.Add(message);  //缓冲消息,暂不发送                return;            }          Conversation.Switchboard.SendTextMessage(message);  //发送消息         }
        //重新邀请        private bool ReInvite()        {            if (!Conversation.Switchboard.IsSessionEstablished)  //如果发现Session已经断开(Conversation已经关闭)            {                _clientform.Dicconversation.Remove(_conversation);                RemoveEvent();                _conversation = _conversation.Messenger.CreateConversation();  //重新简历Conversation                _clientform.Dicconversation.Add(_conversation, this);                AddEvent();                foreach (string account in _leftusers)                {                    _conversation.Invite(account);  //邀请退出了的用户                }                _leftusers.Clear();                return true;            }            return false;        }

最后发自内心的赞一句:CSDN的新博客真是酷毙了

原创粉丝点击