在上一篇提到了4个问题,现在开始回答第三个第四个问题。由于篇幅问题。这里就设置成了上下两篇
消息回执
这个是第三个问题,如何做消息回执。
消息回执分为两种:
1、普通消息
2、延迟消息
3、离线消息
普通消息
普通消息是客户端正常的点对点发送聊天消息。格式大致如下:
客户端接收到系统发送的消息后,应该回执如下内容
-
<message id="V4NkR-38" to="8ntmorv1ep4wgcy"
-
from="[email protected]/Spark 2.6.3#android" type="crs"/>
延迟消息:
延时消息是指,系统推送聊天消息后。接收方没有回执,系统则会把消息返回发送方。这种消息不需要客户端回执。
此消息内容如下:
-
<message id="HncqL-52" to="[email protected]/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="[email protected]"
-
from="[email protected]/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="[email protected]/Spark 2.6.3#android" type="crs">
-
<offline xmlns="urn:xmpp:offline"/>
-
</message>
这里面的type类型crs也是本人做的XMPP拓展了。
对于消息回执本人有两种解决方案。
首先看下消息回执的处理流程图吧:

呵呵,画的很丑。
方案一:
将不同设备的终端标志存储起来,谁读过一个的消息,记录他的设备id。当他再次登陆不同设备的时候,从数据库查找它没有读过的记录。这里计较绕口。但是本人并不推崇,这里主要是提供一个思路。
OK,首先来看个图。本人画得并不好
时序图:

哈哈~又画的很丑
根据本人在项目中的业务情况。比较重要的系统消息(第三方系统发送的消息)需要做回执。普通chat可以不用做回执。所以下面分为系统消息会普通消息
系统消息:
服务端发送者:
-
<message id="gJE6T-2" to="[email protected]" from="8ntmorv1ep4wgcy">
-
<body>{"optType":"add","groupId":"testroomta100","groupName":"test房间100","subject":"我是主题","description":"测试","admin":"600788","userList":"600788,test1","msgType":10001}</body>
-
</message>
客户端返回消息接收状态报告
-
<message
-
from='[email protected]'
-
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="[email protected]/Spark 2.6.3"
-
from="[email protected]/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="[email protected]/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="[email protected]/Spark 2.6.3"
-
type="crs">
-
<received xmlns="urn:xmpp:receipts">200</received>
-
</message>
B客户端不在线(等待10s,OF给A返回消息发送失败)
-
<message id="V4NkR-38" to="[email protected]/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="[email protected]" from="[email protected]/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="[email protected]/Smack#android" type="crs">
-
<received xmlns="urn:xmpp:receipts">201</received>
-
</message>
群组消息
在线
OF发送消息:
-
<message id="2859-1" to="[email protected]" 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="[email protected]"
-
type="csgo">
-
<received xmlns='urn:xmpp:receipts'>200</received>
-
</message>
离线
OF发送消息
-
<message id="8954-6" to="[email protected]" 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>[email protected]/Smack#android</jid>
-
</bind>
-
</iq>