引入###
即时通信技术现在已经广泛应用到各种app中了,那么他到底是怎么实现的呢。前段时间因为项目开发的需要,了解学习过一些这方面。这篇文章记录的是我个人了解的一些这方面的知识,如果有错误的地方还请斧正指出,非常感谢。
另外 慕课网中 郭大神的关于xmpp的这个课程也对我学习起到了很大的帮助:http://www.imooc.com/learn/223
xmpp###
- XMPP的前身是Jabber,一个开源形式组织产生的网络即时通信协议。XMPP目前被IETF国际标准组织完成了标准化工作。标准化的核心结果分为两部分; 核心的XML流传输协议 基于XML流传输的即时通讯扩展应用 XMPP的核心XML流传输协议的定义使得XMPP能够在一个比以往网络通信协议更规范的平台上。借助于XML易于解析和阅读的特性,使得XMPP的协议能够非常漂亮。 XMPP的即时通讯扩展应用部分是根据IETF在这之前对即时通讯的一个抽象定义的,与其他业已得到广泛使用的即时通讯协议,诸如AIM,QQ等有功能完整,完善等先进性。
- 上面是xmpp的官方解释,其实我每次看完官方解释,总是懵懵的。好像什么都知道了,也好像什么都不知道。不过在我了解过之后简单总结看来,xmpp就像上面说的,是一套协议。里面定义了很多xml格式的文本。比如当我们要登录到服务器时,怎么告诉服务器呢。按照xmpp协议中的规范封装一段xml的字符串以流的方式传给服务器就好了。以最简单的发送一个消息举例:
<message
to='romeo@example.net'
from='juliet@example.com/balcony'
type='chat'
xml:lang='en'>
<body>Wherefore art thou, Romeo?</body>
</message>
- suke 发送给 beta 一条消息,消息的类型是chat(聊天),消息的内容是 who are you ?。
- 总结以上,在我理解看来,xmpp 就像是我们平时说话使用的语言,服务器和客户端都遵循这样的规范,比我message 代表一个消息。这样到客户端想给服务器发送一条消息时,就拼装一个以message开头的xml字符串流到服务器。服务器按照xmpp的协议,就知道这是一条消息。听起来感觉傻乎乎的。可是xmpp就是这样的。
- 如果想深入了解xmpp协议,请搜索《xmpp权威指南》,英文版本和中文版本的都有。
IM即时通信软件该怎么做###
- 目前网上比较主流的是采用开源的 openfire服务器+smack sdk 进行开发。smack是一个封装了tcp连接和xmpp协议解析的sdk。
smack的资料可以移步github:https://github.com/igniterealtime/Smack/wiki/Smack-4.2-Readme-and-Upgrade-Guide
里面提供了详尽的API - 我们都知道即时通信是需要做长连接的,而openfire和smack之间的连接采用的是tcp长连接。大家一定都知道心跳包和保持在线,断线重连之类的,我刚开始入手的时候也不知道怎么弄。看了文章开头郭大神的那个基于xmpp的推送后,对于其中的原理不能说豁然开朗吧,也算窥探到了一二。我在这里就不赘述了,那个视频已经讲的很明白了。
Smack怎么用###
-
连接到服务器。
- Constant.HOST_SERVER 为服务器IP地址,运行demo时需要修改。
InetAddress addr = InetAddress.getByName(Constant.HOST_SERVER); HostnameVerifier verifier = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return false; } }; DomainBareJid serviceName = JidCreate.domainBareFrom(Constant.HOST_SERVER); XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder() .setHost(Constant.HOST_SERVER) // it will be resolved by setHostAddress method .setUsernameAndPassword(username, pwd) .setPort(Constant.HOST_PORT) .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled) .setXmppDomain(serviceName) .setHostnameVerifier(verifier) .setHostAddress(addr) .setDebuggerEnabled(true) .build(); connection = new XMPPTCPConnection(config); connection.connect();
-
连接之后,就是登录和获取好友列表(花名册)了。具体的实现
-
roster.addRosterLoadedListener(new RosterLoadedListener() {});这个是添加一个监听,因为再刚登录的时候,直接去获取花名册,服务器返回的经常为空集合。网上有人建议在登录成功之后让线程睡100毫秒,不过还是添加listener优雅一点。
if (connection.isConnected()) { try { connection.login(); } catch (Exception e) { android.os.Message msg = android.os.Message.obtain(); msg.what = LOGIN_FAILED; handler.sendMessage(msg); } // Thread.sleep(100); Roster roster = Roster.getInstanceFor(connection); roster.addRosterLoadedListener(new RosterLoadedListener() { @Override public void onRosterLoaded(Roster roster) { Logger.e(TAG,"Get roster success ... " ); android.os.Message msg = android.os.Message.obtain(); msg.what = LOGIN_SUCCESS; msg.obj = roster; handler.sendMessage(msg); } @Override public void onRosterLoadingFailed(Exception exception) { Logger.e(TAG,"Get roster failed : " + exception.getMessage()); } }); android.os.Message msg = android.os.Message.obtain(); msg.what = LOGIN_SUCCESS; msg.obj = roster; handler.sendMessage(msg); } else { android.os.Message msg = android.os.Message.obtain(); msg.what = CONNECTION_FAILED; handler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); }
-
-
创建聊天室
private void createChatRoom() { // 设置聊天监听器,监听聊天消息 chatManager = ChatManager.getInstanceFor(connection); chatManager.addIncomingListener(mIncomingChatMessageListener); } IncomingChatMessageListener mIncomingChatMessageListener = new IncomingChatMessageListener() { //接收消息 @Override public void newIncomingMessage(EntityBareJid from, Message message, org.jivesoftware.smack.chat2.Chat chat) { String str = chatMap.get(from.toString()); String name = from.getLocalpart().toString(); String msg = message.getBody(); String type = message.getType().toString(); if (str == null) { str = name + " : " + type + " : " + msg + "\n"; } else { str += name + " : " + type + " : " + msg + "\n"; } chatMap.put(from.toString(), str); mainBinding.tvChatCon.setText(str); } }; //发送消息 private void sendmsg(String msg, RosterEntry rosterEntry, Message.Type type) { Chat currentChat; try { String jidStr = rosterEntry.getJid().toString(); EntityBareJid jid = JidCreate.entityBareFrom(jidStr); currentChat = chatManager.chatWith(jid); Message newMessage = new Message(); newMessage.setBody(msg); newMessage.setType(type); currentChat.send(newMessage); String str = chatMap.get(jidStr); if (str == null) { str = username + " : " + msg + "\n"; } else { str += username + " : " + msg + "\n"; } chatMap.put(jidStr, str); mainBinding.tvChatCon.setText(str); mainBinding.etWillSendMsg.setText(""); } catch (SmackException.NotConnectedException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (XmppStringprepException e) { e.printStackTrace(); } }
总结###
其实对于xmpp,我现在也只是略微了解一些,上面这些,希望对有这方面需求的同学有所帮助。郭大神的那个视频和xmpp权威指南真的很值得阅读。视频有助于理解客户端与服务器的连接实现,而xmpp权威指南中会告诉你xmpp协议都定义了那些功能(节的概念,发送消息,出席状态,状态订阅,多设备登录哪个优先,发送文件是怎么实现的,为什么只能发送小文件等等很多很多),了解了这些功能也有助于使用smack这个sdk,同时给扩展做好准备。
好了,希望以上这些,可以对大家有些许帮助,谢谢。