C#连接基于Java开发IM——Openfire
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.dll 中 xmppclientconnection 类进行连接,就算你代码毫无错误,也无法正常连接openfire,因为
博主只是对源码改了一句话,即可正常连接。
修改 protocol 中 sasl 下的 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
欢迎关注我的博客获得第一时间更新
我的github:steveneco
我的博客园:warrenryan
上一篇: 新买的鞋,我是不是买到假货了!