文档章节

openfire smack消息回执设置,处理掉包问题

penngo
 penngo
发布于 2014/10/31 00:07
字数 979
阅读 9538
收藏 16

在网络不稳定时,openfire容易出现掉包情况,原因是在客户端掉线时,openfire并不能马上知道客户端已经断线,至于要多久才能发现客户端断线,跟服务器端设置的Idle Connections 时间有关。默认为360秒。

为解决掉包问题,xmpp协议支持消息回执,这个只需在客户端发消息时设置要求回执就行,服务器端不需要另外设置。

使用smack设置消息回执方法

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已经收到

上边的方法只是客户端对客户端的消息回执,另外也可以在服务器端发送回执给客户端,告诉客户端已经收到消息

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="test1@xxx.com" from="test2@xxx.com/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="test2@xxx.com/pc" from="test1@xxx.com"><received xmlns="urn:xmpp:receipts" id="e3539-31"/></message>


开发者在使用时,也可以根据业务需要定义自己的回执格式。


© 著作权归作者所有

penngo

penngo

粉丝 81
博文 116
码字总数 68412
作品 2
广州
高级程序员
私信 提问
加载中

评论(6)

penngo
penngo 博主

引用来自“qq1257167”的评论

自定义回执格式会有问题,客户端收到的和服务器发送的不一致
服务器:<message to="test@127.0.0.1/SmackTest" from="admin/REQUEST" type="chat"><event xmlns="jabber:server" kind="msgRequest" id="5s75F-4"/></message>
客户端:<message to="test@127.0.0.1/SmackTest" from="admin/REQUEST" type="chat"><thread>2c44457b-8831-4cf7-a94c-a9358769ec73</thread><event xmlns="jabber:server"></event></message>
这种情况如何解决

引用来自“penngo”的评论

消息都是有id属性值,为什么你的客户端发的回执消息没有id属性值?

引用来自“qq1257167”的评论

你用的那方式的消息也没消息id啊。我试了很多的服务器往客户端发消息的方式,客户端收到的结果都是一样的,后面那部分(kind="msgRequest" id="5s75F-4")没了,求解决方式,但是用()这种系统固定的没问题,就是自定义回执格式有问题。但是客户端往客户端发送这种自定义的回执可以成功,不会出现我说的那种问题。
你发的数据格式与我的也不一样,不确定你是不是格式弄错了。
q
qq1257167

引用来自“qq1257167”的评论

自定义回执格式会有问题,客户端收到的和服务器发送的不一致
服务器:<message to="test@127.0.0.1/SmackTest" from="admin/REQUEST" type="chat"><event xmlns="jabber:server" kind="msgRequest" id="5s75F-4"/></message>
客户端:<message to="test@127.0.0.1/SmackTest" from="admin/REQUEST" type="chat"><thread>2c44457b-8831-4cf7-a94c-a9358769ec73</thread><event xmlns="jabber:server"></event></message>
这种情况如何解决

引用来自“penngo”的评论

消息都是有id属性值,为什么你的客户端发的回执消息没有id属性值?
你用的那方式的消息也没消息id啊。我试了很多的服务器往客户端发消息的方式,客户端收到的结果都是一样的,后面那部分(kind="msgRequest" id="5s75F-4")没了,求解决方式,但是用()这种系统固定的没问题,就是自定义回执格式有问题。但是客户端往客户端发送这种自定义的回执可以成功,不会出现我说的那种问题。
penngo
penngo 博主

引用来自“qq1257167”的评论

自定义回执格式会有问题,客户端收到的和服务器发送的不一致
服务器:<message to="test@127.0.0.1/SmackTest" from="admin/REQUEST" type="chat"><event xmlns="jabber:server" kind="msgRequest" id="5s75F-4"/></message>
客户端:<message to="test@127.0.0.1/SmackTest" from="admin/REQUEST" type="chat"><thread>2c44457b-8831-4cf7-a94c-a9358769ec73</thread><event xmlns="jabber:server"></event></message>
这种情况如何解决
消息都是有id属性值,为什么你的客户端发的回执消息没有id属性值?
q
qq1257167
自定义回执格式会有问题,客户端收到的和服务器发送的不一致
服务器:<message to="test@127.0.0.1/SmackTest" from="admin/REQUEST" type="chat"><event xmlns="jabber:server" kind="msgRequest" id="5s75F-4"/></message>
客户端:<message to="test@127.0.0.1/SmackTest" from="admin/REQUEST" type="chat"><thread>2c44457b-8831-4cf7-a94c-a9358769ec73</thread><event xmlns="jabber:server"></event></message>
这种情况如何解决
penngo
penngo 博主

引用来自“tanranran”的评论

有一种情况是 对方不在线,你发送消息到服务器.但是不管成功失败,服务都不会给你返回东西,这个是最坑的地方.
两种方法解决: 1,客户端记录发送的消息,如果在指定时间内没有收到对方回复,就判断对方为不在线。 2、服务器判断到对方不在线,把对方不在线信息返回给发送者。
tanranran
tanranran
有一种情况是 对方不在线,你发送消息到服务器.但是不管成功失败,服务都不会给你返回东西,这个是最坑的地方.
openfire 服务端讯息回执添加和响应

openfire 服务端消息回执添加和响应 openfire本身有很多比较好的实现框架,如asmack和smack。具体的大家可以上网搜索。因为openfire本身对长连接的支持不是很好,而且相对来说,长连接也挺消...

今幕明
2014/08/23
2.4K
1
即时通讯 openfire掉包

android客户端,openfire,XMPP。 在openfire向客户端发送消息的时候会出现掉包的问题。客户端打电话,网络信号不好等等原因都会造成这个问题。所以现在想解决掉包的这个问题。不知道有没有现...

黑狗
2013/04/11
3.5K
5
【openfire,smack使用总结】--Smack库的使用

Smack介绍 Smack是XMPP协议的的实现库,Smack库易于使用,仅仅几行代码就能实现客户端连接,登陆,发送即时消息。但是由于使用XMPP协议,所以不适合用在高并发的场合。 Smack的使用 本文使用...

e_one
2017/03/09
0
0
openfire添加好友流程

请教openfire添加好友可否通过服务器进行? 当前我的分析是:openfire添加好友是客户端与客户端进行认证的,比如A客户端添加B为好友,则发送消息到达B客户端,B客户端根据自身添加好友的sub...

lynattemm
2014/10/09
1K
0
当Smack意外掉线时Openfire服务器需要一定时间才能知道?

1、当Smack意外掉线时,它的状态改变需要经过一定的时间(大概是30秒左右)Openfire服务器才能获取,所以在这个空档期发送的消息,Openfire 服务器还认为Smack在线,会继续发送消息给Smack,...

Zee
2014/03/25
2.2K
1

没有更多内容

加载失败,请刷新页面

加载更多

UAVStack功能上新:新增JVM监控分析工具

UAVStack推出的JVM监控分析工具提供基于页面的展现方式,以图形化的方式展示采集到的监控数据;同时提供JVM基本参数获取、内存dump、线程分析、内存分配采样和热点方法分析等功能。 引言 作为...

宜信技术学院
29分钟前
6
0
MySQL的5种时间类型的比较

日期时间类型 占用空间 日期格式 最小值 最大值 零值表示 DATETIME 8 bytes YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00 9999-12-31 23:59:59 0000-00-00 00:00:00 TIMESTAMP 4 bytes YYYY-MM......

物种起源-达尔文
36分钟前
7
0
云服务OpenAPI的7大挑战,架构师如何应对?

阿里妹导读:API 是模块或者子系统之间交互的接口定义。好的系统架构离不开好的 API 设计,而一个设计不够完善的 API 则注定会导致系统的后续发展和维护非常困难。比较好的API设计样板可以参...

阿里云官方博客
39分钟前
5
0
Rancher + VMware PKS实现全球数百站点的边缘K8S集群管理

Sovereign Systems是一家成立于2007年的技术咨询公司,帮助客户将传统数据中心技术和应用程序转换为更高效的、基于云的技术平台,以更好地应对业务挑战。曾连续3年提名CRN,并且在2012年到2...

RancherLabs
44分钟前
5
0
6、根据坐标,判断该坐标是否在地图区域范围内

最近在写配送区域相关的代码,具体需求如下: 根据腾讯地图划分配送区域,总站下边设多个配送分站,然后将订单中的收货地址将其分配给不同的配送分站。 1、地图区域划分(腾讯地图) 1.1、H...

有一个小阿飞
46分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部