【0】README
1)本文旨在 给出源代码 实现 smack client + openfire server 实现 peer to peer communication
2)当然,代码中用到的 user 和 pass, 你需要事先在 openfire 里面注册;
3)also , you can checkout the source code from
Attention)要区分 ChatManagerListener and ChatMessageListener, 这两个监听器,它们的功能是不同的,但是长相却十分相似;
【2】代码如下
package com.xmpp.client;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Body;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smackx.delay.packet.DelayInformation;
// this class encapsulates some info for
// connection, login, creating chat.
public class UserChatBase { // smakc client base class.
private XMPPTCPConnectionConfiguration conf;
private AbstractXMPPConnection connection;
private ChatManager chatManager;
private Chat chat;
/**
* @param args refers to an array with ordered values as follows: user, password, host, port.
*/
public UserChatBase(String... args) {
String username = args[0];
String password = args[1];
conf = XMPPTCPConnectionConfiguration.builder().setUsernameAndPassword(username, password)
.setServiceName(MyConstants.HOST)
.setHost(MyConstants.HOST)
.setPort(Integer.valueOf(MyConstants.PORT))
.setSecurityMode(SecurityMode.disabled) // (attention of this line about SSL authentication.)
.build();
connection = new XMPPTCPConnection(conf);
chatManager = ChatManager.getInstanceFor(connection);
// differentiation ChatManagerListener from ChatMessageListener
chatManager.addChatListener(new MyChatListener(this));
}
/**
* connect to and login in openfire server.
* @throws XMPPException
* @throws IOException
* @throws SmackException
*/
public void connectAndLogin() throws SmackException, IOException, XMPPException {
System.out.println("executing connectAndLogin method.");
System.out.println("connection = " + connection);
connection.connect();
System.out.println("successfully connection.");
connection.login(); // client logins into openfire server.
System.out.println("successfully login.");
}
/**
* disconnect to and logout from openfire server.
* @throws XMPPException
* @throws IOException
* @throws SmackException
*/
public void disconnect() {
System.out.println("executing disconnect method.");
try {
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* create chat instance using ChatManager.
*/
public Chat createChat(String toUser) {
toUser += "@" + MyConstants.HOST;
chat = chatManager.createChat(toUser);
// create the chat with specified user.(startup a thread)
MessageHandler handler = new MessageHandler(chat);
MessageHandler.Sender sender = handler.new Sender(); // creating inner class.
new Thread(sender).start();
// creating thread over.
return chat;
}
/**
* get chat timestamp, also time recoded when the msg starts to send.
* @param msg
* @return timestamp.
*/
public String getChatTimestamp(Message msg) {
ExtensionElement delay = DelayInformation.from(msg);
if(delay == null) {
return null;
}
Date date = ((DelayInformation) delay).getStamp();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
return format.format(date);
}
public XMPPTCPConnectionConfiguration getConf() {
return conf;
}
public AbstractXMPPConnection getConnection() {
return connection;
}
public ChatManager getChatManager() {
return chatManager;
}
public Chat getChat() {
return chat;
}
}
package com.xmpp.client;
import java.util.Locale;
public class ClientA { // one client
public static void main(String a[]) throws Exception {
Locale.setDefault(Locale.CHINA);
UserChatBase client = new UserChatBase(
new String[]{"tangtang", "tangtang"});
client.connectAndLogin();
System.out.println("building connection between tangtang as sender and pacoson as receiver.");
// create the chat with specified user.(startup a thread)
client.createChat("pacoson");
}
}
package com.xmpp.client;
import java.util.Locale;
public class ClientB { // another client.
public static void main(String a[]) throws Exception {
Locale.setDefault(Locale.CHINA);
UserChatBase client = new UserChatBase(
new String[]{"pacoson", "pacoson"});
client.connectAndLogin();
System.out.println("building connection between pacoson as sender and tangtang as receiver.");
// create the chat with specified user.(startup a thread)
client.createChat("tangtang");
}
}
package com.xmpp.client;
import java.util.Set;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManagerListener;
import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Body;
// 监听器
public class MyChatListener implements ChatManagerListener{
private UserChatBase client;
public MyChatListener(UserChatBase client) {
this.client = client;
}
@Override
public void chatCreated(Chat chat, boolean createdLocally) {
if (!createdLocally) {
chat.addMessageListener(new ChatMessageListener() {
@Override
public void processMessage(Chat chat, Message message) {
String from = message.getFrom();
Set<Body> bodies = message.getBodies();
String timestamp = client.getChatTimestamp(message);
if(timestamp != null) {
System.out.println(timestamp);
}
for(Body b : bodies) {
System.out.println(from + ":" + b.getMessage());
}
}
});
}
}
}
package com.xmpp.client;
public class MyConstants { // 常量类
public static final int PORT = 5222;
public static final String HOST = "lenovo-pc";
public static final String PLUGIN_PRESENT_URL
= "http://lenovo-pc:9090/plugins/presence/status?";
//= "http://lenovo-pc:9090/plugins/presence/status?jid=pacoson@lenovo-pc&type=text&req_jid=tangtang@lenovo-pc";
public static final String buildPresenceURL(String from, String to, String type) {
return PLUGIN_PRESENT_URL
+ "jid=" + to + "@" + HOST + "&"
+ "req_jid=" + from + "@" + HOST + "&"
+ "type=" + type;
}
}
package com.xmpp.client;
import java.util.Scanner;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.chat.Chat;
// 创建发送msg 线程.
public class MessageHandler {
private Chat chat;
public MessageHandler(Chat chat) {
this.chat = chat;
}
class Sender implements Runnable{
/*public Sender(Chat chat) {
MessageHandler.this.chat = chat;
}*/
public Sender() {}
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()) {
String line = scanner.nextLine();
try {
chat.sendMessage(line);
} catch (NotConnectedException e) {
e.printStackTrace();
break;
}
}
}
}
class Receiver {
}
}
(干货——只有当 消息接收者处于离线的时候,其接收到的消息才会封装 delay 元素,其属性有 stamp 记录了 msg 发送的时间。(also, you can refer to https://community.igniterealtime.org/thread/22791))