C#連線基於Java開發IM——Openfire

WarrenRyan發表於2019-02-20

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
出  處:https://www.cnblogs.com/WarrenRyan/
關於作者:熱愛數學、熱愛機器學習,喜歡彈鋼琴的不知名小菜雞。
版權宣告:本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。若需商用,則必須聯絡作者獲得授權。
特此宣告:所有評論和私信都會在第一時間回覆。也歡迎園子的大大們指正錯誤,共同進步。或者直接私信
聲援博主:如果您覺得文章對您有幫助,可以點選文章右下角推薦一下。您的鼓勵是作者堅持原創和持續寫作的最大動力!


博主一些其他平臺:
微信公眾號:寤言不寐
BiBili——小陳的學習記錄
Github——StevenEco
BiBili——記錄學習的小陳(計算機考研紀實)
掘金——小陳的學習記錄
知乎——小陳的學習記錄

聯絡方式:

電子郵件:cxtionch@live.com

社交媒體聯絡二維碼:

C#連線基於Java開發IM——Openfire

相關文章