openfire smack消息回執設置,處理掉包問題

在網絡不穩定時,openfire容易出現掉包狀況,緣由是在客戶端掉線時,openfire並不能立刻知道客戶端已經斷線,至於要多久才能發現客戶端斷線,跟服務器端設置的Idle Connections 時間有關。默認爲360秒。java

爲解決掉包問題,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中查看數據session

test2發送消息給test1,消息id爲Winlh-55app


test1發送回執給test2,告訴test2消息Winlh-55已經收到dom

上邊的方法只是客戶端對客戶端的消息回執,另外也能夠在服務器端發送回執給客戶端,告訴客戶端已經收到消息ide

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消息回執協議:this

發送者message加上<request xmlns='urn:xmpp:receipts'/>要求接收者發送回執spa

<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>


開發者在使用時,也能夠根據業務須要定義本身的回執格式。

相關文章
相關標籤/搜索