Android基于XMPP协议之实现即时通讯的原理
来源:互联网 发布:磁盘碎片整理 知乎 编辑:程序博客网 时间:2024/05/21 11:02
一、xmpp协议
xmpp可以理解为可扩展的消息和出席协议(eXtensible Messageing and Presence Protocol).出席即可以理解为用户的在线的状态,消息则是服务器与客户端互相通信的消息;常见的xmpp服务器有openfire、Ejabberd等,这里我们用的是openfire;
二、xmpp寻址(jid)
xmpp在网络上通信的每个实体都有统一的ID表示,即为JID(jabber identifier);JID有很多不同的形式,通常类似于邮件的形式;
JID=[ node”@” ] domain [ “/” resource ]
node: 用户名
domain:服务器域名itcast.cn
resource:属于用户的位置或设备
一个用户可以同时以多种资源与一个服务器连接
laowang@itcast.cn/spark
jige@itcast.cn/smack
三、xmpp的传输数据格式
在xmpp中,数据的传输和识别是通过在一个xmpp流上发送和接收xmpp节点完成的,这三个节点分别是presence、message、iq;这三个节点都有通用的属性from、to、type、id;xmpp传输数据也是通过xml文档实现的,该文档始终有一个根节点stream;下面给出一个简单的消息的例子:
<stream:stream> <iq type='get'> <query xmlns='jabber:iq:roster'/> </iq> <presence type=’available’> <message from='laowang@itcast.cn' /></stream:stream>
四、presence节点
presence节点主要显示该用户的在线状态和与其相关的好友的状态设置,控制并且报告JID的可访问性
Mode: 在线chat、离线away 、离开xa、请勿打扰dnd
Type:available, unavailable,subscribe,subscribed,unsubscribe,Unsubscribed,error
普通presence节
普通presence节不含type属性,或者type属性值为unavailable或error。type属性没有available值,因为可以通过缺少type属性来指出这种情况。
用户通过发送不带to属性,直接发往服务器的presence节来操纵自己的出席状态。
<presence/><presence type='unavailable'/><presence> <show>away</show> <status>at the ball</status><presence/><presence> <status>touring the countryside</status> <priority>10</priority></presence><presence> <priority>10</priority></presence>
简单解释:
(1)show元素用来传达用户的可访问性,只能出现在presence节中。该元素的值可以为:away、chat、dnd和xa,分别表示离开、有意聊天、不希望被打扰和长期离开。
(2)status元素时一个人类可读的字符串,在接收者的聊天客户端中,这个字符串一般会紧挨着联系人名字显示。
(3)priority元素用来指明连接资源的优先级,介于-128~127,默认值为0。
五、message节点
message节是一个实体向另一个实体发送消息;消息类型有不同的type,例如chat、error、normal、groupchat等等;如果没有指定type,默认就是normal;尽管message节可以包含任意扩展元素,但body和thread元素是为向消息中添加内容提供的正常机制。这两种子元素均是可选的。
六、IQ节点
IQ节点表示的是info/query,为xmpp通信提供了请求和响应的机制;它与http协议的基本工作原理很相似,允许获取和设置查询,与http的get和post请求类似,它有两种请求两种响应,如下:
Get: 获取当前域值
Set: 设置或替换get查询的值
Result: 说明成功的响应了先前的查询
Error: 查询和响应中出现的错误
七、连接生命周期
在开始发送任何节之前,必须建立xmpp流,必须先建立通往xmpp服务器的socket连接,建立通往xmpp服务器的连接,xmpp流就启动了。通过向服务器发送起始元素stream节,就可打开xmpp流。服务器通过发送响应流起始标记stream进行相应,一旦双向建立xmpp流,就可以来回发送各种元素。当用户结束xmpp会话时,会终止会话并断开连接。一般终止会话方式是,首先发送无效出席信息,然后关闭stream元素。
xmpp的通信流程
(1)节点连接到服务器;(2)服务器利用本地目录系统中的证书对其认证;(3)节点指定目标地址,让服务器告知目标状态;(4)服务器查找、连接并进行相互认证;(5)节点之间进行交互.
八、简单的好友聊天功能实现
1>.连接服务器,建立连接
这里将与服务器的连接封装一个类,用户将连接、xmpp对象和方法的获取等功能封装在一起,ConnectionConfiguration用户获取和openfire服务器的连接,XMPPConnection获取xmpp类,以便于做出连接的操作,还有通过XMPPConnection调用登录方法,调用获取好友列表的方法getRoster,获取聊天的管理类ChatManager,监听器监听来自不同好友的消息addPacketListener,下线操作disconnect, 具体我们先来看看:
public class ConnectionManager { private static final String HOST = "192.168.138.1"; private static final int PORT = 5222; private ConnectionManager() {} private static ConnectionManager sInstance = new ConnectionManager(); //当前用户的帐号 private long mAccount; private ConnectionConfiguration mConfiguration; private XMPPConnection mConnection; public long getAccount() { return mAccount; } public static ConnectionManager getInstance() { return sInstance; } //通过XmppConnection去链接Openfire服务器 public void connect() throws Exception { mConfiguration = new ConnectionConfiguration(HOST, PORT); mConfiguration.setDebuggerEnabled(true); mConnection = new XMPPConnection(mConfiguration); mConnection.connect(); } public void login(String account, String password) throws Exception { mConnection.login(account, password); } //提供获取好友列表控制类的方法 public Roster getRoster() { return mConnection.getRoster(); } public ChatManager getChatManager() { return mConnection.getChatManager(); } //监听器监听来自不同好友的消息 public void addPacketListener(PacketListener packetListener,PacketFilter packetFilter){ mConnection.addPacketListener(packetListener, packetFilter); } //通知服务器下线 public void disConnect() { mConnection.disconnect(); }}
2>开启欢迎页调用连接的方法与服务器获取连接
ConnectionManager.getInstance().connect();==========//通过XmppConnection去链接Openfire服务器 public void connect() throws Exception { mConfiguration = new ConnectionConfiguration(HOST, PORT); mConfiguration.setDebuggerEnabled(true); mConnection = new XMPPConnection(mConfiguration); mConnection.connect(); }
3>连接成功后,前往登陆页登录,通过xmppconnection调用xmpp登录方法
mConnectionManager.login(account, password);==========public void login(String account, String password) throws Exception { mConnection.login(account, password);}
4>进入主界面,显示会话信息和联系人界面,先看联系人界面
//获取联系人数据源mRoster = ConnectionManager.getInstance().getRoster();//设置监听,添加或者删除好友后要刷新列表和数据mRoster.addRosterListener(mRosterListener);==========private RosterListener mRosterListener = new RosterListener() { //添加好友后 @Override public void entriesAdded(Collection<String> addresses) { Log.i(TAG, "entriesAdded"); for (String userJid : addresses) { Log.i(TAG, userJid); } Utils.runInUIThread(new Runnable() { @Override public void run() { setAdapter(); } }); } //更新好友后 @Override public void entriesUpdated(Collection<String> addresses) { Log.i(TAG, "entriesUpdated"); for (String userJid : addresses) { Log.i(TAG, userJid); } Utils.runInUIThread(new Runnable() { @Override public void run() { setAdapter(); } }); } //删除好友后 @Override public void entriesDeleted(Collection<String> addresses) { Log.i(TAG, "entriesDeleted"); for (String userJid : addresses) { Log.i(TAG, userJid); } Utils.runInUIThread(new Runnable() { @Override public void run() { setAdapter(); } }); } @Override public void presenceChanged(Presence presence) { Log.i(TAG, "presenceChanged"); if (presence != null) { Log.i(TAG, presence.toXML()); } } };
5>会话界面,首先要显示会话信息,将收到的消息存入数据库,可以回显
mConnectionManager.addPacketListener(new PacketListener() { @Override public void processPacket(Packet packet) { Message message = (Message) packet; //好友发来的消息保存到数据库 String from = message.getFrom(); String userJid = from.substring(0, from.lastIndexOf("/")); Msg msg = new Msg(Msg.TYPE_MSG_RECEIVE,userJid , message.getBody()); try { MyDbUtils.saveMsg(msg); } catch (Exception e) { e.printStackTrace(); } Utils.runInUIThread(new Runnable() { @Override public void run() { setAdapter(); } }); } }, new PacketFilter() { //消息过滤,只接受Message类型的packet @Override public boolean accept(Packet packet) { return packet instanceof Message; } }); }
6>消息对话界面
进入消息对话洁面后,首先获取聊天的工具类Chat
//得到ChatManager去发起聊天,监听消息的到来//mFriendUserJid为当前聊天对象的jidmConnectionManager = ConnectionManager.getInstance();//获取创建与当前好友对话的工具类mChat = mChatManager.createChat(mFriendUserJid, mMessageListener);//接受消息的监听,从好友哪里接受消息,processMessage----->被PacketReader调用//只能接受当前mFriendUserJid发来的消息 private MessageListener mMessageListener = new MessageListener() { @Override public void processMessage(Chat chat, Message message) { //Utils.showToast(getApplicationContext(), message.toXML()); Msg msg = new Msg(Msg.TYPE_MSG_RECEIVE,mFriendUserJid, message.getBody()); mMsges.add(msg); //保存消息 try { MyDbUtils.saveMsg(msg); } catch (Exception e) { e.printStackTrace(); } Utils.runInUIThread(new Runnable() { @Override public void run() { setAdapter(); } }); } };
然后是向当前对象发送消息
//发送消息 public void send(View view) { final String content = mInput.getText().toString().trim(); if (TextUtils.isEmpty(content)) { return; } mInput.setText(""); //消息类型,消息的to,消息from,消息内容,消息的发送时间 Utils.runInThread(new Runnable() { @Override public void run() { try { mChat.sendMessage(content); } catch (Exception e) { e.printStackTrace(); Utils.showToast(getApplicationContext(), "消息发送失败"); } } }); Msg msg = new Msg(Msg.TYPE_MSG_SEND,mFriendUserJid, content); mMsges.add(msg); try { //存储消息到数据库方便回显 MyDbUtils.saveMsg(msg); } catch (Exception e) { e.printStackTrace(); } setAdapter(); }//在聊天界面退出的时候移除MessageListener @Override protected void onDestroy() { super.onDestroy(); mChat.removeMessageListener(mMessageListener); }
- Android基于XMPP协议之实现即时通讯的原理
- 基于XMPP协议的即时通讯工具的客户端实现原理
- 基于XMPP协议的即时通讯工具的客户端实现原理
- Android基于XMPP协议的即时通讯
- 基于XMPP协议的即时通讯
- Android之基于XMPP协议即时通讯软件(一)
- Android之基于XMPP协议即时通讯软件(二)
- Android之基于XMPP协议即时通讯软件(三)
- Android之基于XMPP协议即时通讯软件(一)
- Android之基于XMPP协议即时通讯软件(二)
- Android之基于XMPP协议即时通讯软件(一)
- Android之基于XMPP协议即时通讯软件(二)
- Android之基于XMPP协议即时通讯软件(三)
- Android之基于XMPP协议即时通讯软件(一)
- Android之基于XMPP协议即时通讯软件(二)
- Android之基于XMPP协议即时通讯软件(三)
- Android之基于XMPP协议即时通讯软件(一)
- Android之基于XMPP协议即时通讯软件(三)
- C语言字符串函数的模拟实现(strlen、strcpy、strcat、strstr 、strcmp)
- cartographer源码分析(13)-transform-rigid_transform.h
- [HDU]-6040 Hints of sd0061
- 编写一个简单的内核模块
- 学习笔记2
- Android基于XMPP协议之实现即时通讯的原理
- Java 泛型的类型擦除和桥方法
- ArrayList VS LinkedList
- Node.js +Swagger Editor + Swagger-UI 环境搭建
- 线程池的使用和底层实现
- Unity学习笔记
- 二叉树--判断一棵二叉树是否是平衡二叉树&&求一颗二叉树的镜像
- cartographer源码分析(14)-transform-transform.h
- android异常处理