目录
即时通信( Instant Messaging,简称 IM)
官网 http://www.openfire.net.cn/1329.html
即时通信( Instant Messaging,简称 IM)
像qq.微信 , 即时通讯需要 服务器, 客户端, 服务器与客户端通讯
一般支持私聊,群聊,离线消息等等
XMPP协议背景:
即时通讯没有统一的标准【各用各的通讯协议,就像qq无法给微信发消息】,无法实现互联互通, XMPP协议就是这样的一个协议 (支持文本、视频、文件等的传输) ,
XMPP 协议是以 XML 为基础的
<message from="11@myopenfire.com" to="22@myopenfire.com">11发给22的消息</message>
也就是说 XMPP 协议不仅可以用在人与人之间的交流,而且可以实现软件与软件或软件与人之间的交流
XMPP 中定义了三个角色, 服务器,客户端,网关
XMPP服务端: openfire服务端
官网 http://www.openfire.net.cn/1329.html
Openfire 是基于XMPP 协议的IM 的服务器端的一个实现
参考 http://myopenfire.com/article/articledir/1
Openfire是一个Java应用程序,
Openfire采用内置的jetty作web服务器,在启动AdminConsolePlugin插件时调用startup()方法启动jetty服务器,9090为其明文端口,9091为其加密端口。 启动成功后, 可以从网站进入http://ip:9090 openfire管理控制台
设置服务端端口,客户端连接设置,策略设置,离线消息的存储
openfire需要用到数据库:
ofGroup (用户组的数据)
ofGroupUser (组成员)
ofUser (用户数据)
ofOffline (离线邮件存储)
插件开发
下载源码,生成eclipse项目,添加项目依赖的Jar包,运行openfire
XMPP客户端:Spark
这里就不介绍spark了, 这个spark软件的通讯核心是用Smack, 主要介绍Smack的开发使用
Smack 是一个XMPP 协议的Java 实现,提供一套可扩展的API
你需要添加smack相关的jar包:
smack.jar
smackx.jar
org.jivesoftware.smack.
Spark 提供了客户端一个基本的实现,并提出了一个很好的插件架构
服务器通讯要用自定义证书的话要用自定义的SSLConent,说白点就是不验证证书是否可信。这玩意不知道使用有CA签名的证书会不会有这问题。
例: java客户端发送消息
package com.openfire.client;
import java.io.IOException;
import java.util.Scanner;
import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatManagerListener;
import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
/**
* Hello XMPP!
*
*/
public class App extends Thread
{
void say() {
};
public static void main( String[] args ) throws SmackException, IOException, XMPPException, InterruptedException
{
// 对连接的配置
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
builder.setUsernameAndPassword("admin", "123456");
builder.setServiceName("ww110"); //openfire服务端名字,登录管理后台可查看
builder.setHost("192.168.0.110");
builder.setPort(5222);
// 不加这行会报错,因为没有证书
builder.setSecurityMode(SecurityMode.disabled);
//SASLAuthentication.supportSASLMechanism("PLAIN",0);
builder.setDebuggerEnabled(true);
XMPPTCPConnectionConfiguration config = builder.build();
// 建立连接并登陆
AbstractXMPPConnection c = new XMPPTCPConnection(config);
c.addPacketSendingListener(new StanzaListener() {
@Override
public void processPacket(Stanza st) throws NotConnectedException {
System.out.println("in StanzaListener:" + st.getFrom());
}
}, new StanzaFilter() {
@Override
public boolean accept(Stanza st) {
System.out.println("in StanzaFilter:" + st.getFrom());
return false;
}
});
c.connect();
c.login();
ChatManager chatManager = ChatManager.getInstanceFor(c);
chatManager.addChatListener(new ChatManagerListener() {
@Override
public void chatCreated(Chat cc, boolean bb) {
// 当收到来自对方的消息时触发监听函数
cc.addMessageListener(new ChatMessageListener() {
@Override
public void processMessage(Chat cc, Message mm) {
System.out.println("app2收到消息:" + cc + mm.getBody());
}
});
}
});
while (true) {
sleep(2000);
//每2秒给new1用户发消息
Chat chat = chatManager.createChat("new1@ww110");
chat.getThreadID(); Message msg = new Message();
msg.setBody("你好,我是机器人");
chat.sendMessage(msg);
}
}
}
import java.util.Collection;
import java.util.Iterator;
import javax.net.SocketFactory;
import org.jivesoftware.smack.AccountManager;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Session;
import org.jivesoftware.smack.packet.Message.Type;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* <b>function:</b> 利用Smack框架完成 XMPP 协议通信
*/
public class SmackXMPPTest {
/** 连接器 */
private Connection connection;
/** 连接参数 */
private ConnectionConfiguration config;
/** openfire服务器address */
private final static String serverIp = "127.0.0.1";
/**
* <b>function:</b> 消息处理方法
*/
private final void fail(Object o) {
if (o != null) {
System.out.println(o);
}
}
private final void fail(Object o, Object... args) {
if (o != null && args != null && args.length > 0) {
String s = o.toString();
for (int i = 0; i < args.length; i++) {
String item = args[i] == null ? "" : args[i].toString();
if (s.contains("{" + i + "}")) {
s = s.replace("{" + i + "}", item);
} else {
s += " " + item;
}
}
System.out.println(s);
}
}
/**
* <b>function:</b> 初始Smack对openfire服务器链接的基本配置
*/
@Before
public void init() {
try {
/** 5222是openfire服务器默认的通信端口,可以登录http://ip:9090/到管理员控制台查看客户端到服务器端口 */
config = new ConnectionConfiguration(serverIp, 5222);
config.setCompressionEnabled(true); /** 是否启用压缩 */
config.setSASLAuthenticationEnabled(true);/** 是否启用安全验证 */
config.setDebuggerEnabled(false);/** 是否启用调试 */
//config.setReconnectionAllowed(true);
//config.setRosterLoadedAtLogin(true);
//config.setSendPresence(true);
connection = new XMPPConnection(config);
/** 建立连接 */
connection.connect();
} catch (XMPPException e) {
e.printStackTrace();
}
fail(connection);
fail(connection.getConnectionID());
}
/**
* <b>function:</b> 销毁连接器
*/
@After
public void destory() {
if (connection != null) {
connection.disconnect();
connection = null;
}
}
/**
* <b>function:</b> XMPPConnection 获取连接基本信息
*/
@Test
public void testConnection() {
AccountManager accountManager = connection.getAccountManager();
for (String attr : accountManager.getAccountAttributes()) {
fail("AccountAttribute: {0}", attr);
}
fail("AccountInstructions: {0}", accountManager.getAccountInstructions());
fail("isConnected:", connection.isConnected());/** 是否链接 */
fail("isAnonymous:", connection.isAnonymous());
fail("isAuthenticated:", connection.isAuthenticated());/** 是否有权限 */
fail("isSecureConnection:", connection.isSecureConnection());
fail("isUsingCompression:", connection.isUsingCompression());/** 是否使用压缩 */
}
/**
* <b>function:</b> 用户管理器:创建用户,修改密码
*/
@Test
public void testAccountManager() {
AccountManager accountManager = connection.getAccountManager();
for (String attr : accountManager.getAccountAttributes()) {
fail("AccountAttribute: {0}", attr);
}
fail("AccountInstructions: {0}", accountManager.getAccountInstructions());
fail("supportsAccountCreation: {0}", accountManager.supportsAccountCreation());
/** 可以在管理员控制台页面http://ip:9090/user-summary.jsp查看用户/组的相关信息,来查看是否成功创建用户 */
try {
/** 创建一个用户boy,密码为boy */
accountManager.createAccount("boy", "boy");
/** 修改密码 */
accountManager.changePassword("abc");
} catch (XMPPException e) {
e.printStackTrace();
}
}
/**
* <b>function:</b> 用户登陆
*/
@Test
public void testUser() {
try {
/** 用户登陆,用户名、密码 */
connection.login("boy", "boy");
} catch (XMPPException e) {
e.printStackTrace();
}
fail("User:", connection.getUser());/** 获取当前登陆用户 */
Roster roster = connection.getRoster();/** 所有用户组 */
/** 好友用户组,
用Spark添加用户好友,这里就可以查询到相关的数据 */
Collection<RosterEntry> rosterEntiry = roster.getEntries();
Iterator<RosterEntry> iter = rosterEntiry.iterator();
while (iter.hasNext()) {
RosterEntry entry = iter.next();
fail("组: {0}, 名: {1}, 状态: {2}, 类型: {3}, 用户: {4}", entry.getGroups(), entry.getName(), entry.getStatus(), entry.getType(), entry);
}
/** 未处理、未验证的好友 */
Collection<RosterEntry> unfiledEntries = roster.getUnfiledEntries();
iter = unfiledEntries.iterator();
while (iter.hasNext()) {
RosterEntry entry = iter.next();
fail("组: {0}, 名: {1}, 状态: {2}, 类型: {3}, 用户: {4}", entry.getGroups(), entry.getName(), entry.getStatus(), entry.getType(), entry);
}
}
/**
* <b>function:</b> 更改登录状态,向其他用户发消息
*/
@Test
@SuppressWarnings("static-access")
public void testPacket() {
try {
connection.login("testUser", "testPassword");
} catch (XMPPException e) {
e.printStackTrace();
}
//Packet packet = new Data(new DataPacketExtension("jojo@" + serverIp, 2, "this is a message"));
//connection.sendPacket(packet);
/** 更改用户状态,
available=true表示在线,false表示离线,
status状态签名;
登陆后在Spark客户端软件中就可以看到你登陆的状态 */
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("我在线了");
connection.sendPacket(presence);
Session session = new Session();
String sessid = session.nextID();
connection.sendPacket(session);
/** 向user2@127.0.0.1发送聊天消息,Spark登陆的jojo用户就可以接收到消息
**/
/** Type.chat 表示聊天,groupchat多人聊天,error错误,headline在线用户; */
Message message = new Message("user2@" + serverIp, Type.chat);
//Message message = new Message(sessid, Type.chat);
message.setBody("user2, 我是testUser!");
connection.sendPacket(message);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* <b>function:</b> 更改登录状态,向其他用户发消息,监听其他用户发过来的信息
*/
@Test
public void testChatManager() {
/** 设置状态 */
try {
connection.login("testUser", "testPassword");
} catch (XMPPException e) {
e.printStackTrace();
}
/** 设置状态 */
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("我在线了");
connection.sendPacket(presence);
/** 获取当前登陆用户的聊天管理器 */
ChatManager chatManager = connection.getChatManager();
/** 为指定用户创建一个chat,MyMessageListeners用于监听对方发过来的消息 */
Chat chat = chatManager.createChat("user2@" + serverIp, new MyMessageListeners());
try {
/** 发送消息 */
chat.sendMessage("h!~ user2……");
/** 用message对象发送消息 */
Message message = new Message();
message.setBody("message");
message.setProperty("color", "red");
chat.sendMessage(message);
} catch (XMPPException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* <b>function:</b> 消息监听器,用户监听对方发送的消息,也可以想对方发送消息
*/
class MyMessageListeners implements MessageListener {
public void processMessage(Chat chat, Message message) {
try {
/** 发送消息 */
chat.sendMessage("dingding……" + message.getBody());
} catch (XMPPException e) {
e.printStackTrace();
}
/** 接收消息 */
fail("From: {0}, To: {1}, Type: {2}, Sub: {3}", message.getFrom(), message.getTo(), message.getType(), message.toXML());
fail("body: ", message.getBody());
}
}
}