C# 连接基于 Java 开发 IM——Openfire

本贴最后更新于 2205 天前,其中的信息可能已经时过境迁

Openfire 简介

   Openfire 是开源的、基于可拓展通讯和表示协议(XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。
  Server 和 Client 端的通信都用 xml 文档的形式进行通信。
但是 Openfire 是 Java 语言写的,对于 C#的 dll 拓展库相比与 java 的 jar 包少的可怜,在网上寻找一番之后找到了一个比较好的 dll 拓展库,agsxmpp 是一个专门为 C#连接 xmpp 协议下即时通讯已经搭建 xmpp 协议服务端的的 dll,同时他有商业版 MatriX,博主穷学生一个,没有啥钱去购买商业版,还是采用了普通的 agsxmpp。

AgsXmpp 简介

  agsxmpp 是 AG—Software 进行开发的一个开源项目,可以在它的官网进行下载源码。
  agsxmpp 是在 2003 年开始研发,2008 年发布它的最后一个版本,因此它在兼容性上显然是不很好的。
  同时在 C#连接 Openfire 上,agsxmpp 中有一个巨坑,加上网上关于 agsxmpp 的开发文档奇少,而且博主没有在官网上找到相关的开发文档(就算有也是全英文看不懂系列),故记下开发全过程。
  因为 agsxmpp 并不是专门为 Openfire 制作的,而是对任何以 xmpp 协议的即时通讯进行连接等服务。如果不对源码进行一定的重写,在某些情况下会出现一些问题。
  如果你直接使用 agsxmpp.dllXmppClientConnection 类进行连接,就算你代码毫无错误,也无法正常连接 Openfire,因为
博主只是对源码改了一句话,即可正常连接。
修改 protocolsasl 下的 Mechanism.cs 中源码,将

case "DIGEST-MD5":
    return MechanismType.DIGEST_MD5;

注释,因为 openfire 发送数据流 是通过 PLAIN 的 , 而 agsxmpp 是默认是 通过 DIGEST-MD5 发送。
  同时,在 agsxmpp 中,还有一个地方表现了对 openfire 的不兼容,openfire 发送 iq 节 不接收 to 属性,因此还需要修改一个地方
源代码如下

public IQ SendIq(agsXMPP.protocol.client.IQ iq, int timeout)
{
    synchronousResponse = null;
    AutoResetEvent are = new AutoResetEvent(false);

    SendIq(iq, new IqCB(SynchronousIqResult), are);

    if (!are.WaitOne(timeout, true))
    {
        // Timed out
        lock (m_grabbing)
        {
            if (m_grabbing.ContainsKey(iq.Id))
                m_grabbing.Remove(iq.Id);
        }
        return null;
    }

    return synchronousResponse;
}
 
修改后如下
public void SendIq(IQ iq, IqCB cb, object cbArg)
{
    // check if the callback is null, in case of wrong usage of this class
    if (cb != null)
        {
            TrackerData td = new TrackerData();
            td.cb = cb;
            td.data = cbArg;
            m_grabbing[iq.Id] = td;

            //iq在agsxmpp中发送Iq节的时候先iq.RemoveAttribute("to")
            iq.RemoveAttribute("to");
        }
    m_connection.Send(iq);
}

public void SendIq2(IQ iq, IqCB cb, object cbArg)
{
    // check if the callback is null, in case of wrong usage of this class
    if (cb != null)
        {
            TrackerData td = new TrackerData();
            td.cb = cb;
            td.data = cbArg;
            m_grabbing[iq.Id] = td;
            //iq在agsxmpp中发送Iq节的时候先iq.RemoveAttribute("to")
            //iq.RemoveAttribute("to");
        }
    m_connection.Send(iq);
}

  登录操作:发送 xml 消息用 SendIq() 方法
  其他操作:发送 xml 消息用 SendIq2() 方法

连接上 Openfire

官方提供了一个只有三行代码的小型 Demo

XmppClientConnection xmpp = new XmppClientConnection(server);
xmpp.Open(username,secret);
xmpp.OnLogin+=delegate(object o){xmpp.Send(new Message(JID,MessageType.chat,msg));};

我的代码

public class XmppLogin
    {
        private XmppClientConnection xmppCon;
        private bool isSSL;
        /// <summary>
        /// 是否使用加密连接
        /// </summary>
        public bool IsSSL { get { return isSSL; } set { isSSL = value; } }
        private string userName;
        private string server;
        public string Server { get { return server; } set { server = value; } }
        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName { get { return userName; } set { userName = value; } }
        private string passWord;
        /// <summary>
        /// 密码
        /// </summary>
        public string PassWord { get { return passWord; } set { passWord = value; } }
        private string clientVersion;
        /// <summary>
        /// 客户端版本
        /// </summary>
        public string ClientVersion { get { return clientVersion; }set { clientVersion = value; } }
        /// <summary>
        /// 登录状态
        /// </summary>
        public string LoginState { get { return xmppCon.XmppConnectionState.ToString(); } }
        private int port;
        /// <summary>
        /// 登录端口,通常是5222,加密时是5223
        /// </summary>
        public int Port { get { return port; }set{ port = value;} }

        public XmppLogin()
        {
            xmppCon = new XmppClientConnection();
        }

        #region 传递一个XmppClient对象
        /// <summary>
        /// 传递一个XmppClient对象
        /// </summary>
        /// <param name="con">需要操作的具体实例</param>
        public XmppLogin(XmppClientConnection con)
        {
            xmppCon = new XmppClientConnection();
            xmppCon = con;
        }
        #endregion

        #region 登录
        /// <summary>
        /// 登录openfire的方法
        /// </summary>
        /// <returns>返回值为是否登录</returns>
        public void Login()
        {
            
            xmppCon.Server = server;
            xmppCon.UseSSL = false;
            xmppCon.Port = 5222;
            xmppCon.AutoResolveConnectServer = true;
            xmppCon.UseCompression = false;
            xmppCon.EnableCapabilities = true;
            xmppCon.ClientVersion = "1.0";
            xmppCon.Capabilities.Node = "http://www.ag-software.de/miniclient/caps";
            xmppCon.DiscoInfo.AddIdentity(new DiscoIdentity("pc", "MyClient", "client"));
            xmppCon.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.DISCO_INFO));
            xmppCon.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.DISCO_ITEMS));
            xmppCon.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.MUC));
            xmppCon.Open(userName,passWord);
            
            //xmppCon.OnLogin += delegate (object o) { xmppCon.Send(new agsXMPP.protocol.client.Message("testa@118.89.48.159", MessageType.chat, "sdgo")); };
        }
        #endregion

        #region 测试连接
        /// <summary>
        /// 测试指定的OpenFire服务器和端口是否能连通
        /// </summary>
        /// <returns>返回是否能连通</returns>
        public bool TestPing()
        {
            string ipAddress = Server;
            int portNum = port;
            bool CanConnect = false;
            IPAddress ip = IPAddress.Parse(ipAddress);
            try
            {
                IPEndPoint point = new IPEndPoint(ip, portNum);
                using (Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                {
                    sock.Connect(point);
                    CanConnect = sock.Connected;
                    sock.Close();
                    return CanConnect;
                }
            }
            catch (SocketException e)
            {
                //LOG TODO
                return false;
            }
        }
        #endregion

        public static implicit operator XmppClientConnection(XmppLogin v)
        {
            return v.xmppCon;
        }
    }

  至此,Openfire 连接成功。
  最近忙而且也刚开始弄这个,过几天更新一下 XmppConnection 下各种属性、事件、函数的具体用法。

我的掘金:WarrenRyan

我的简书:WarrenRyan

欢迎关注我的博客获得第一时间更新 https://blog.tity.online

我的 Github:StevenEco

  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 97 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8214 回帖
  • C#
    29 引用 • 34 回帖 • 5 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...