• .net平台 基于 XMPP协议的即时消息服务端简单实现


    .net平台 基于 XMPP协议的即时消息服务端简单实现

            昨天抽空学习了一下XMPP,在网上找了好久,中文的资料太少了所以做这个简单的例子,今天才完成。公司也正在准备开发基于XMPP协议的即时通讯工具所以也算是打一个基础吧!如果你还没有了解过XMPP请先阅读附录中链接的文章,本实例是基agsXMPP上开发的,agsXMPP是C#写的支持开源XMPP协议软件,我们可以在agsXMPP上快速构建自已的即时通讯平台,我的这个例子只是修改了服务器端,因为agsXMPP本身自带的服务器端没有实现聊天功能、签名和登录密码认证。

    服务器端XmppSeverConnection类事件

    //在流开始时触发,一般是最初的响应流
    streamParser.OnStreamStart += new StreamHandler(streamParser_OnStreamStart);

    //在流结束时触发,一般是发送</stream:stream>并关闭套接字连接streamParser.OnStreamEnd += new StreamHandler(streamParser_OnStreamEnd);

    //在接收到流结点时触发,这是用得最多的,常用的<message>消息,<Presence>出席消息,< IQ>请求应答消息都在这里处理
    streamParser.OnStreamElement += new StreamHandler(streamParser_OnStreamElement);

     

    //此处处理大部份的消息,包括消息路由
    private void streamParser_OnStreamElement(object sender, Node e)
            {
                Console.WriteLine("OnStreamElement: " + e.ToString());
                if (e.GetType() == typeof(Presence))
                {
                 // 路由presences节
                }
                else if (e.GetType() == typeof(Message))
                {
                    // 路由messages节
                }
                else if (e.GetType() == typeof(IQ))
                {
                    //处理IQ节
                }
            }

     

     

     /// <summary>
            /// IQ节处理函数
            /// </summary>
            /// <param name="iq">.</param>
            private void ProcessIQ(IQ iq)
            {
                if (iq.Query.GetType() == typeof(Auth))
                {
                   Auth auth = iq.Query as Auth;
                    this.Username = auth.Username.ToString();

                    switch (iq.Type)
                    {
                        case IqType.get:
                            iq.SwitchDirection();
                            iq.Type = IqType.result;
                            auth.AddChild(new Element("password"));
                            auth.AddChild(new Element("digest"));
                            Send(iq);
                            break;

                        case IqType.set:
                            // 进行登录认证
                            if (AccountBus.CheckLogin(auth.Username, auth.Digest, this.SessionId))
                            {
                                iq.SwitchDirection();
                                iq.Type = IqType.result;
                                iq.Query = null;
                                Send(iq);

                                Console.WriteLine(auth.Username + "登录了" + "   登录时间:" + System.DateTime.Now.ToString());

                            }
                           else
                            {
    //登录失败返回错误信息
                                iq.SwitchDirection();
                                iq.Type = IqType.error;
                                iq.Query = null;
                                Send(iq);
                            }
                            break;
                    }
                }
                else if (iq.Query.GetType() == typeof(Roster))
                {
                    ProcessRosterIQ(iq);
                }
            }

     

    /// <summary>
            /// 处理IQ节的杂项数据.
            /// </summary>
            /// <param name="iq">The iq.</param>
            private void ProcessRosterIQ(IQ iq)
            {
                if (iq.Type == IqType.get)
                {
                    // 发送IQ节的杂项数据
                  //这里我用来下载好友列表
                    iq.SwitchDirection();
                    iq.Type = IqType.result;
                    List<string> friendList = new List<string>();
                    friendList = AccountBus.GetFriendName(this.username);
                    foreach (string str in friendList)
                    {
                        RosterItem ri = new RosterItem();
                        ri.Name = str.Trim();
                        ri.Subscription = SubscriptionType.both;
                        ri.Jid = new agsXMPP.Jid(str.Trim() + "@localhost");
                        ri.AddGroup("localhost");
                        iq.Query.AddChild(ri);
                    }
                    Send(iq);
                }
            }


     

    服务器端开启监听5222端口

     

     while (running)
                    {
                        ////
                        allDone.Reset();
                        // Start an asynchronous socket to listen for connections.
                        Console.WriteLine("等待连接");

                        listener.BeginAccept(new AsyncCallback(AcceptCallback), null);

                        //// 等待客户端连接                    
                         allDone.WaitOne();
                    }


    如果收到客户端请求就异步调用AcceptCallback初始化套接字连接
    ,并为客户端建立一个通信线程,新建初始化套接字连接采用异步调
    用读取套接字信息

     public XmppSeverConnection(Socket sock)
                : this()
            {
                m_Sock = sock;
                m_Sock.BeginReceive(buffer, 0, BUFFERSIZE, 0, new AsyncCallback(ReadCallback), null);
                m_Sock.SendTimeout = 100;
            }



    客户端与服务器端的交互过程


      1客户端异步向服务器端发送连接请求

    <stream:stream to='localhost' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xml:lang='en'>

    2服务器端收到请求,初始化回应流,并随机生成一相SessionID

     <stream:stream xmlns:stream="http://etherx.jabber.org/streams" from="localhost" id="30e3b8c0" >

    3等待服务器返回消息后客户端发送用户名(由于在客户端采用了异步调用
    方式,所以UI界面感觉不到等待)

    <iq xmlns="jabber:client" id="agsXMPP_1" type="get" to="localhost">
    <query xmlns="jabber:iq:auth"><username>test</username></query></iq>

    4服务器端收到用户名等待用户提供密码

    <iq xmlns="jabber:client" from="localhost" type="result" id="agsXMPP_1">
    <query xmlns="jabber:iq:auth"><username>test</username><password />
    <digest /></query></iq>

    5客户端提供加密后的密码

    <iq xmlns="jabber:client" id="agsXMPP_2" to="localhost" type="set">
    <query xmlns="jabber:iq:auth"><username>test</username>
    <digest>e66557d2b67256bf7e9b317a51b6101674a56b5e</digest>
    <resource>MiniClient</resource></query></iq>

    6服务器端从数据库验证用户名和密码,并返回结果

    iq xmlns="jabber:client" from="localhost" type="result" id="agsXMPP_2" />

    7如果返回错误,客户端提示并终断连接,否则客户端发送响应数据

    8 服务器端返回数据

    9 客户端发送状态,

    10服务器收到状态,发送IQ节并通知其它用户.

    项目解决方案和类图


    附录:
    推荐使用Pandion作为客户端
    服务器端下载      客户端下载
    agsXMPP 源码下载 
    XMPP RFC 3920 可扩展消息出席协议 
    jabber官方网站

     
    分类: XMPP实践
  • 相关阅读:
    关于jsp页面是放在webroot目录下和web-inf下优缺点
    eclipse查看jar包中class的中文注释乱码问题的解决
    如何在eclipse里使用git
    ****JFinal 部署在 Tomcat 下推荐方法
    jfinal框架教程-学习笔记(二)
    Struts2 标签库讲解
    struts2 标签库 介绍
    VC无闪烁刷屏技术的实现【转】
    小知识:SPI四种模式区别【转】
    如何在Android 或Linux 下,做Suspend /Resume 的Debug【转】
  • 原文地址:https://www.cnblogs.com/endv/p/5451833.html
Copyright © 2020-2023  润新知