在上一篇提到了4个问题,现在开始回答第三个第四个问题。由于篇幅问题。这里就设置成了上下两篇
消息回执
这个是第三个问题,如何做消息回执。
消息回执分为两种:
1、普通消息
2、延迟消息
3、离线消息
普通消息
普通消息是客户端正常的点对点发送聊天消息。格式大致如下:
<message id="V4NkR-38" type="chat" to="test1@8ntmorv1ep4wgcy/Spark 2.6.3#android" Form="test2@8ntmorv1ep4wgcy/Spark 2.6.3#android" > <body>.......</body> </message>
客户端接收到系统发送的消息后,应该回执如下内容
<message id="V4NkR-38" to="8ntmorv1ep4wgcy" from="test1@8ntmorv1ep4wgcy/Spark 2.6.3#android" type="crs"/>
延迟消息:
延时消息是指,系统推送聊天消息后。接收方没有回执,系统则会把消息返回发送方。这种消息不需要客户端回执。
此消息内容如下:
<message id="HncqL-52" to="user1@8ntmorv1ep4wgcy/Spark 2.6.3#user11387952379387" from="8ntmorv1ep4wgcy" type="error"> <body>{"time":"2013-12-25T14:20:01 032Z","sd":"sd"} </body> <delay xmlns="urn:xmpp:delay" current="2013-12-25T14:20:31 038Z">501</delay> </message>
离线消息:
接收方不在线的时候,需要接收到的消息被系统存储。当客户端登陆的时候,推送给接收者。
该消息推送后,需要接收方回执
离线消息内容
<message id="HncqL-65" to="1aaa@8ntmorv1ep4wgcy" from="user1@8ntmorv1ep4wgcy/Spark 2.6.3" type="chat"> <body>{"time":"2013-12-25T14:25:43 963Z","sd":"sd"}</body> <offline xmlns="urn:xmpp:offline" current="2013-12-25T06:26:45.501Z"/> </message>
客户端回执规范(需要添加离线namespace说明)
<message id="V4NkR-38" to="8ntmorv1ep4wgcy" from="test1@8ntmorv1ep4wgcy/Spark 2.6.3#android" type="crs"> <offline xmlns="urn:xmpp:offline"/> </message>
这里面的type类型crs也是本人做的XMPP拓展了。
对于消息回执本人有两种解决方案。
首先看下消息回执的处理流程图吧:
呵呵,画的很丑。
方案一:
将不同设备的终端标志存储起来,谁读过一个的消息,记录他的设备id。当他再次登陆不同设备的时候,从数据库查找它没有读过的记录。这里计较绕口。但是本人并不推崇,这里主要是提供一个思路。
OK,首先来看个图。本人画得并不好
时序图:
哈哈~又画的很丑
根据本人在项目中的业务情况。比较重要的系统消息(第三方系统发送的消息)需要做回执。普通chat可以不用做回执。所以下面分为系统消息会普通消息
系统消息:
服务端发送者:
<message id="gJE6T-2" to="600788@8ntmorv1ep4wgcy" from="8ntmorv1ep4wgcy"> <body>{"optType":"add","groupId":"testroomta100","groupName":"test房间100","subject":"我是主题","description":"测试","admin":"600788","userList":"600788,test1","msgType":10001}</body> </message>
客户端返回消息接收状态报告
<message from='600788@8ntmorv1ep4wgcy' id='gJE6T-2' type=’chat’ to=''> <received xmlns='urn:xmpp:receipts' type=200/> </message>
OpenFire与客户端消息交互内容示例:
聊天消息:
1、在线(模拟client A、B对话)。
A发送消息给Of服务端:
<message id="V4NkR-38" to="test2@8ntmorv1ep4wgcy/Spark 2.6.3" from="test1@8ntmorv1ep4wgcy/Spark 2.6.3" type="chat"> <body> {"time":"2013-11-20T06:41:36.102Z", "cnt":"你想说什么呢?","msgTypec":"10000","mtype":"0"} </body> <thread>P2J5R2</thread> </message>
Of服务端返回A消息
<message id="V4NkR-38" to="test1@8ntmorv1ep4wgcy/Spark 2.6.3#android" from="8ntmorv1ep4wgcy" type="crs"> <body> {"time":"2013-11-20T06:42:23.761Z","msgType":"10013"} </body> </message>
B客户端在线返回状态
<message id="V4NkR-38" to="8ntmorv1ep4wgcy" from="test2@8ntmorv1ep4wgcy/Spark 2.6.3" type="crs"> <received xmlns="urn:xmpp:receipts">200</received> </message>
B客户端不在线(等待10s,OF给A返回消息发送失败)
<message id="V4NkR-38" to="test1@8ntmorv1ep4wgcy/Spark 2.6.3#android" from="8ntmorv1ep4wgcy" type="error"> <body> {"time":"2013-11-20T06:58:58.558Z","cnt":"你想说什么呢?", "msgTypec":"10000","mtype":"0"} </body> <delay xmlns="urn:xmpp:delay" current="2013-11-20T07:05:44.445Z"> 501 </delay> </message>
2、OF给出的消息离线
<message id="p4y4N-38" to="test2@8ntmorv1ep4wgcy" from="test1@8ntmorv1ep4wgcy/Spark 2.6.3" type="chat"> <body> {"time":"2013-11-20T07:17:17.816Z","cnt":"你想说什么呢? ","msgTypec":"10000","mtype":"0"} </body> <thread>h3P0lk</thread> <offline xmlns="urn:xmpp:offline" current="2013-11-20T07:36:24.363Z"/> </message>
客户端返回离线报告
<message id="p4y4N-38" to="8ntmorv1ep4wgcy" from="test2@8ntmorv1ep4wgcy/Smack#android" type="crs"> <received xmlns="urn:xmpp:receipts">201</received> </message>
群组消息
在线
OF发送消息:
<message id="2859-1" to="test2@8ntmorv1ep4wgcy" from="8ntmorv1ep4wgcy" type="sgo"> <body> {"optType":"add","groupId":"BBC0","time":"2013-11-20T08:16:40.214Z", "groupName":"test房间0","subject":"我是主题", "description":"测试","admin":"test2", "userList":"test2,test1","msgType":10001} </body> </message>在线客户端回复:
<message id="6238-5" to="8ntmorv1ep4wgcy" from="test2@8ntmorv1ep4wgcy" type="csgo"> <received xmlns='urn:xmpp:receipts'>200</received> </message>
离线
OF发送消息
<message id="8954-6" to="test1@8ntmorv1ep4wgcy" from="8ntmorv1ep4wgcy" type="sgo"> <body> {"optType":"add","groupId":"BBCAD0", "time":"2013-11-20T08:24:30.583Z","groupName":"test房间0", "subject":"我是主题", "description":"测试", "admin":"test2","userList":"test2,test1","msgType":10001} </body> <offline xmlns="urn:xmpp:offline" current="2013-11-20T08:28:17.639Z"/> </message>
新建表结构
ofSysMsgHistory(系统消息历史记录表):
字段名 |
类型 |
说明 |
msgId |
varchar2(15) |
消息id(主键) |
mtype |
varchar2(10) |
消息类型(级别): 0、系统升级(SUG), 1、系统公告(SP), 1、群组操作(SGO), 2、聊天消息(CHAT), 4、业务消息(SPB) |
stanza |
varchar2(200) |
消息内容 |
creationDate |
Char(15) |
创建时间 |
ofGroupOptHistory(个人群组操作历史信息表)
字段名 |
类型 |
说明 |
msgId |
varchar2(15) |
消息id(主键) |
userId |
varchar2(15) |
接受者用户id |
stanza |
varchar2(100) |
消息内容 |
creationDate |
Char(15) |
创建时间 |
ofTerminalForMsg(消息对应设备已读状态关联表)
字段名 |
类型 |
说明 |
msgId |
varchar2(15) |
消息id(主键) |
userId |
varchar2(15) |
用户id(主键) |
TerminalId |
varchar2(15) |
设备id(主键) |
ofUserLogin(用户登陆历史信息)
字段名 |
类型 |
说明 |
userId |
varchar2(15) |
用户id(主键) |
TerminalId |
varchar2(15) |
设备id(主键) |
version |
varchar2(15) |
最近登陆版本号 |
ofClientUpgradeVersion(客户端版本升级表)
字段名 |
类型 |
说明 |
version |
varchar2(15) |
版本号 |
level |
int |
1.重大升级 2.普通升级 |
creationDate |
Char(15) |
升级时间 |
ofTerminalInfo(设备信息表)
字段名 |
类型 |
说明 |
id |
int |
设备id |
desc |
varchar2(15) |
1.Android 2.Iphone windowsPhone |
表关系图:
这个就是几张表之间的关系图了。
下面还有需要修改openfire的jar中的源码,关于tinder部分,本人会单独拿出来写一片博文。在tinder.jar包中 修改Message.Type类型
/** * System Upgrade */ sug, /** * SystemAnnouncement */ sp, /** * group opt */ sgo, /** * Businessmessage */ spb, /** * system time */ st, /** * Clientsreceiving state */ crs; /** * 群组操作返回 */ csgo;
在登陆的时候还需要修改登陆session信息,绑定资源等。
客户端修改绑定资源:
<iq id="use1D-4" type="set"> <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"> <resource>Smack</resource> <terminal>android</terminal> </bind> </iq>
(smack源码修改地址:类SASLAuthentication中bindResourceAndEstablishSession()方法中)
Bind bindResource = new Bind(); bindResource.setResource(resource); bindResource.setTerminal("android"); Bind实体中添加相应属性
服务端返回绑定设置:
<iq type="result" id="use1D-4" to="8ntmorv1ep4wgcy/2c133c9e"> <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"> <jid>test1@8ntmorv1ep4wgcy/Smack#android</jid> </bind> </iq>