在网络不稳定时,openfire容易出现掉包情况,原因是在客户端掉线时,openfire并不能马上知道客户端已经断线,至于要多久才能发现客户端断线,跟服务器端设置的Idle Connections 时间有关。默认为360秒。 为解决掉包问题,xmpp协议支持消息回执,这个只需在客户端发消息时设置要求回执就行,服务器端不需要另外设置。
使用smack设置消息回执方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
package
com.penngo.test;
import
java.awt.EventQueue;
public
class
ReceiptDialog
extends
JDialog {
private
JTextField textField;
/**
* Launch the application.
*/
public
static
void
main(String[] args) {
EventQueue.invokeLater(
new
Runnable() {
public
void
run() {
try
{
ReceiptDialog dialog =
new
ReceiptDialog();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(
true
);
}
catch
(Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the dialog.
*/
public
ReceiptDialog()
throws
Exception{
setBounds(
100
,
100
,
450
,
300
);
getContentPane().setLayout(
null
);
textField =
new
JTextField();
textField.setBounds(
20
,
20
,
301
,
22
);
getContentPane().add(textField);
textField.setColumns(
10
);
Connection.DEBUG_ENABLED =
true
;
// 打开smack debug
ConnectionConfiguration config =
new
ConnectionConfiguration(
"127.0.0.1"
,
5222
);
//52222
config.setSendPresence(
true
);
final
Connection connection =
new
XMPPConnection(config);
// 自动回复回执方法,如果对方的消息要求回执。
ProviderManager pm = ProviderManager.getInstance();
pm.addExtensionProvider(DeliveryReceipt.ELEMENT, DeliveryReceipt.NAMESPACE,
new
DeliveryReceipt.Provider());
pm.addExtensionProvider(DeliveryReceiptRequest.ELEMENT, DeliveryReceipt.NAMESPACE,
new
DeliveryReceiptRequest.Provider());
DeliveryReceiptManager.getInstanceFor(connection).enableAutoReceipts();
// 非自动回复回执方法
// connection.addPacketListener(new PacketListener() {
// public void processPacket(Packet packet) {
// // 监听消息,在检查到对方要求回执时,客户端手动发送回执给对方
// if(packet instanceof Message){
// Message message = (Message)packet;
// PacketExtension receipt = message.getExtension(DeliveryReceiptRequest.ELEMENT, DeliveryReceipt.NAMESPACE);
// if(receipt != null){
// Message receiptMessage = new Message();
// receiptMessage.setTo(message.getFrom());
// receiptMessage.setFrom(message.getTo());
// receiptMessage.addExtension(new DeliveryReceipt(message.getPacketID()));
// connection.sendPacket(receiptMessage);
// }
// }
// }
// }, new PacketFilter() {
// public boolean accept(Packet packet) {
// return true;
// }
// });
connection.connect();
String domain = connection.getServiceName();
// test1登录,发送消息给test2
// String from = "test1";
// final String to = "test2" + "@" + domain;
//test2登录,发送消息给test1
String from =
"test2"
;
final
String to =
"test1"
+
"@"
+ domain;
connection.login(from,
"123456"
,
"pc"
);
// Presence p = new Presence(Presence.Type.available);
// p.setMode(Mode.chat);
// p.setStatus("在线");
// connection.sendPacket(p);
final
Chat chat = connection.getChatManager().createChat(to,
null
);
JButton sendButton =
new
JButton(
"发送"
);
sendButton.addActionListener(
new
ActionListener() {
public
void
actionPerformed(ActionEvent e) {
Message message =
new
Message();
message.setFrom(connection.getUser());
message.setTo(to);
message.setBody(textField.getText());
// 添加回执请求
DeliveryReceiptManager.addDeliveryReceiptRequest(message);
//也可以这样添加回执请求
//DeliveryReceiptRequest deliveryReceiptRequest = new DeliveryReceiptRequest();
//message.addExtension(new DeliveryReceiptRequest());
System.out.println(
"发送======="
+ message.toXML());
try
{
chat.sendMessage(message);
}
catch
(Exception ex){
ex.printStackTrace();
}
}
});
sendButton.setBounds(
331
,
19
,
93
,
23
);
getContentPane().add(sendButton);
}
}
|
运行结果,在smack debug window中查看数据
test2发送消息给test1,消息id为Winlh-55
test1发送回执给test2,告诉test2消息Winlh-55已经收到
上边的方法只是客户端对客户端的消息回执,另外也可以在服务器端发送回执给客户端,告诉客户端已经收到消息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package
com.penngo.openfire;
import
java.io.File;
import
org.dom4j.Element;
import
org.jivesoftware.openfire.session.Session;
import
org.jivesoftware.openfire.XMPPServer;
import
org.jivesoftware.openfire.container.Plugin;
import
org.jivesoftware.openfire.container.PluginManager;
import
org.jivesoftware.openfire.interceptor.InterceptorManager;
import
org.jivesoftware.openfire.interceptor.PacketInterceptor;
import
org.jivesoftware.openfire.interceptor.PacketRejectedException;
import
org.xmpp.packet.Message;
import
org.xmpp.packet.Packet;
public
class
ReceiptPlugin
implements
Plugin, PacketInterceptor{
private
XMPPServer server;
private
String domain;
private
InterceptorManager interceptorManager;
public
void
initializePlugin(PluginManager manager, File pluginDirectory) {
server = XMPPServer.getInstance();
domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
interceptorManager = InterceptorManager.getInstance();
interceptorManager.addInterceptor(
this
);
}
public
void
interceptPacket(Packet packet, Session session,
boolean
incoming,
boolean
processed)
throws
PacketRejectedException {
if
(packet
instanceof
Message && incoming ==
true
&& processed ==
false
){
Message message = (Message)packet;
String to = message.getTo().getNode();
//注意插件中Message类来自tinder.jar包, DeliveryReceipt来自smackx.jar包
// PacketExtension receipt = message.getExtension(DeliveryReceiptRequest.ELEMENT, DeliveryReceipt.NAMESPACE);
Element receipt = message.getChildElement(
"request"
,
"urn:xmpp:receipts"
);
if
(receipt !=
null
){
Message receiptMessage =
new
Message();
receiptMessage.setTo(message.getFrom());
receiptMessage.setFrom(message.getTo());
// Element received = receiptMessage.addChildElement(DeliveryReceipt.ELEMEN, DeliveryReceipt.NAMESPACE);
Element received = receiptMessage.addChildElement(
"received"
,
"urn:xmpp:receipts"
);
received.setAttributeValue(
"id"
, message.getID());
try
{
server.getPacketDeliverer().deliver(receiptMessage);
}
catch
(Exception e){
e.printStackTrace();
}
}
}
}
public
void
destroyPlugin() {
interceptorManager.removeInterceptor(
this
);
}
}
|
xmpp消息回执协议:
发送者message加上<request xmlns='urn:xmpp:receipts'/>要求接收者发送回执
<message id="e3539-31" to="[email protected]" from="[email protected]/pc" type="chat"><body></body><thread></thread><request xmlns='urn:xmpp:receipts'/></message>
接收者在收到消息后回复一条message,并把消息的id放到<received xmlns="urn:xmpp:receipts" id="e3539-31"/>,告诉发送者已经收到
<message to="[email protected]/pc" from="[email protected]"><received xmlns="urn:xmpp:receipts" id="e3539-31"/></message>