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);    }
阅读全文
0 0