服務器:openfire
客戶端程序:smark編寫
首先安裝openfire,下載客戶端後直接安裝即可,數據庫可以用openfire自身的,也可以用自己的數據庫,只要按提示設置好參數即可
之後,就可以用smark寫一個客戶端測試與openfire的通信了(需要引進的jar包除了smark自身的,還要引入xmlpull-1.1.3.1.jar、kxml2-2.3.0.jar兩個包
,作用是解析xml文件)
備註:我用的smark版本是4.0,要引入的基本包有smack-core-4.0.0.jar、smack-debug-4.0.0.jar、smack-extensions-4.0.0.jar、smack-tcp-4.0.0.jar
debug包使用來調試的,tcp是用來初始化連接的、extension包裏面含有發送離線消息、文件等類
下面是創建一個連接
ConnectionConfiguration config = new ConnectionConfiguration("ip", 5222);
//設置成disabled,則不會去驗證服務器證書是否有效,默認爲enabled
config.setSecurityMode(SecurityMode.disabled);
//設置可以調試,默認爲false,老版本的寫法爲XMPPConnection.DEBUG_ENABLED = true;
config.setDebuggerEnabled(true);
//設置是否在登陸的時候告訴服務器,默認爲true
config.setSendPresence(false);
//XMPPConnection在後來的版本中改成了抽象類
XMPPConnection conn = new XMPPTCPConnection(config);
//設置等待時間
conn.setPacketReplyTimeout(5000);
conn.connect();
//用戶名,密碼,資源名(例如:如果是用潘迪安發送的消息,則資源名就是: 潘迪安,用於標識客戶端)
conn.login("admin", "0", "資源名");
關於連接的參數,在新版本中全部在config中設置
發送消息
private void testSendMessage(XMPPConnection conn) throws Exception {
//jid在數據表中ofroster可以查到,一般是 用戶名@服務器名稱
Chat chat = ChatManager.getInstanceFor(conn).createChat("[email protected]", new MessageListener() {
@Override
public void processMessage(Chat chat, Message message) {
System.out.println("Received message: " + message);
}
});
Message msg = new Message();
msg.setBody("hello world");
//定義成normal,在對象不在線時發送離線消息,消息存放在數據表ofoffline中
msg.setType(Message.Type.normal);
//發送消息,參數可以是字符串,也可以是message對象
chat.sendMessage(msg);
//發送廣播
conn.sendPacket(msg);
}
發送離線消息
private void testOffLine(XMPPConnection conn) throws Exception {
//離線文件
OfflineMessageManager offMM = new OfflineMessageManager(conn);
System.out.println("離線文件數量 :" + offMM.getMessageCount());
System.out.println("離線文件內容 :");
//經測試,當調用getMessages時,會觸發chat設置的監聽器,從而輸出離線消息內容,但是getMessages方法返回的離線消息爲空
//猜測回調函數的觸發條件是一個變量,方變量改變時(while(flag)),執行回調函數
List<Message> listMessage = offMM.getMessages();
//listMessage的大小爲0
System.out.println(listMessage.size());
for(Message m : offMM.getMessages()) {
System.out.println(" 離線 : " + m.getBody() + m.getBodies());
}
}
得到好友列表
private void testGetRoster(XMPPConnection conn) throws Exception {
//得到該user的roster(相當於好友列表),不區分是否在線
Roster r = conn.getRoster();
Collection<RosterEntry> c = r.getEntries();
for(RosterEntry re : c) {
StringBuilder sb = new StringBuilder();
sb.append("name : ").append(re.getName());
sb.append("\nuser : ").append(re.getUser());
sb.append("\ntype : ").append(re.getType());
sb.append("\nstatus : ").append(re.getStatus());
System.out.println(sb.toString());
System.out.println("-----------------------------");
}
System.out.println(r.getEntries());
//輸出內容
/* name : null
user : [email protected]
type : from
status : null
-----------------------------
name : null
user : [email protected]
type : to
status : null
-----------------------------
[[email protected], [email protected]]
*/
}
管理好友,監聽好友請求
<pre name="code" class="java">
</pre><pre name="code" class="java">private void testAddAndDelFriends(final XMPPConnection conn) throws Exception {
Roster r = conn.getRoster();
// 用戶的jid,暱稱,用戶的分組。如果該用戶不存在也可以添加
// r.createEntry("[email protected]", "yy", null);
// rosterEntry的構造方法是包訪問權限,不能直接new
// RosterEntry entry = r.getEntry("[email protected]");
// r.removeEntry(entry);
//監聽所有的請求,之後可以過濾掉不想要的請求
PacketListener packetListener = new PacketListener() {
@Override
public void processPacket(Packet packet) throws NotConnectedException {
/*
available
unavailable
subscribe 發出添加好友的請求
subscribed 同意添加好友
unsubscribe 發出刪除好友請求
unsubscribed 刪除好友(即拒絕添加好友),
備註:對方發出添加好友的請求後,在服務器端會自動把對方加入到自己的roster,所以在執行處理好友請求或添加刪除好友的時候,要重新獲取roster,更新好友列表
*/
Presence presence = (Presence) packet;
Type type = presence.getType();
//請求添加好友
if(Type.subscribe.equals(type)) {
//注意點:要設置to(即指明要發送的對象,否則不能成功拒絕),至於from不用設置,因爲在sendPacket方法中已經設置了,formMode初始化的時候爲OMITTED,可以自己設置
/*
switch (fromMode) {
case OMITTED:
packet.setFrom(null);
break;
case USER:
packet.setFrom(getUser());//getUser是抽象方法
break;
*/
//直接用傳來的presence,不能自己新建一個presence(可能要驗證presence是否是原來的對象,來判斷是誰拒絕了誰的好友請求),否則不能成功拒絕對方添加好友
//例:A--presence1-->B A---presence2---C, C---presence3---A這樣服務器就沒辦法判斷是B、C中的哪一個拒絕了A的請求
presence.setType(Type.unsubscribed);//拒絕,發送了一條presence
//presence.setType(Type.unavailable);//發送了兩條presence,一條是subscribed,一條是unavailabled,能接受對方消息,自己的狀態顯示隱身,再一次登錄的時候顯示在線
presence.setTo(presence.getFrom());
presence.setPacketID(presence.getPacketID());
Roster r = conn.getRoster();
try {
RosterEntry entry = r.getEntry(presence.getFrom());
if(entry != null)
r.removeEntry(entry);
} catch (NotLoggedInException | NoResponseException | XMPPErrorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
conn.sendPacket(presence);
//多方刪除自己
} else if(Type.unsubscribe.equals(type)) {
presence.setTo(presence.getFrom());
presence.setType(Type.unsubscribe);
Roster r = conn.getRoster();
try {
r.removeEntry(r.getEntry(presence.getFrom()));
} catch (NotLoggedInException | NoResponseException | XMPPErrorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
conn.sendPacket(presence);
}
}
};
// PacketFilter packetFilter = new PacketFilter() {
//
// //如果返回false,則不把事件交給listener處理,否則會調用packetListener中的processPacket方法
// //方法解釋true if and only if packet passes the filter.
// @Override
// public boolean accept(Packet packet) {
// System.out.println("2" + packet);
// return true;
// }
// };
//過濾掉所有的不是好友請求、刪除的所有packet
PacketFilter packetFilter = new AndFilter(new PacketTypeFilter(Presence.class));
conn.addPacketListener(packetListener, packetFilter);
//未知
RosterExchangeManager rem = new RosterExchangeManager(conn);
rem.addRosterListener(new RosterExchangeListener() {
@Override
public void entriesReceived(String from, Iterator<RemoteRosterEntry> remoteRosterEntries) {
System.out.println(from);
while(remoteRosterEntries.hasNext()) {
RemoteRosterEntry entry = remoteRosterEntries.next();
System.out.println(entry.getUser() + " : " + entry.getName());
}
}
});
}
得到好友的信息,主要是VCard類的使用
private void testGetFriendInfo(XMPPConnection conn) throws Exception {
VCard vCard = new VCard();
VCardManager vcManager = new VCardManager();
//此處返回false
boolean b = vcManager.isSupported("[email protected]", conn);
System.out.println(b);
vCard.load(conn, "[email protected]");
// Load Avatar from VCard
byte[] avatarBytes = vCard.getAvatar();
//得不到頭像等的信息
if(avatarBytes == null) {
return;
}
// To create an ImageIcon for Swing applications
ImageIcon icon = new ImageIcon(avatarBytes);
System.out.println(icon.getIconWidth() + " : " + icon.getIconHeight());
// To create just an image object from the bytes
ByteArrayInputStream bais = new ByteArrayInputStream(avatarBytes);
try {
Image image = ImageIO.read(bais);
FileOutputStream fos = new FileOutputStream("D://icon.jpg");
fos.write(avatarBytes);
fos.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
設置自己的狀態信息
private void testSetInfo(XMPPConnection conn) throws Exception {
VCard vCard = new VCard();
vCard.load(conn);
vCard.setEmailHome("[email protected]");
vCard.setAddressFieldWork("POSTAL", "匯寶大廈");
//修改完要保存修改的內容,否則沒辦法更新到服務器
vCard.save(conn);
//修改自身的狀態,包括隱身,上線(可以指定對特定的好友更改狀態)
Presence p = new Presence(Type.available);
p.setTo("[email protected]");
//修改心情
p.setStatus("我的心情");
//同樣要發到服務器
conn.sendPacket(p);
}
監聽好友的狀態
private void testSetRosterListener(XMPPConnection conn) throws Exception {
Roster r = conn.getRoster();
r.createEntry("[email protected]", "暱稱", null);
r.addRosterListener(new RosterListener() {
@Override
public void presenceChanged(Presence presence) {
//更改狀態信息時調用該方法(更改在線狀態,修改心情,修改頭像等)
System.out.println("presenceChanged");
}
@Override
public void entriesUpdated(Collection<String> addresses) {
//該方法以及下面的方法都是在服務器修改好友信息時觸發
System.out.println("entriesUpdated");
}
@Override
public void entriesDeleted(Collection<String> addresses) {
// TODO Auto-generated method stub
System.out.println("entriesDeleted");
}
@Override
public void entriesAdded(Collection<String> addresses) {
// TODO Auto-generated method stub
System.out.println("entriesAdded");
}
});
}
監聽好友的輸入狀態
private void testGetExtention(XMPPConnection conn) throws Exception {
Chat chat = ChatManager.getInstanceFor(conn).createChat("[email protected]", new MessageListener() {
@Override
public void processMessage(Chat chat, Message message) {
//得到輸入狀態,分爲五種:正在輸入(composing),暫停輸入(paused),發送(active),關閉對話框(gone)
PacketExtension pe = message.getExtension("http://jabber.org/protocol/chatstates");
switch (pe.getElementName()) {
case "composing":
System.out.println("正在輸入......");
break;
case "paused":
System.out.println("正在冥想......");
break;
case "active":
System.out.println("對方已發送。");
break;
case "gone":
System.out.println("對話框已被關閉。");
break;
default:
break;
}
}
});
Message msg = new Message();
msg.addExtension(new ChatStateExtension(ChatState.gone));
msg.setBody("hello world");
chat.sendMessage(msg);
}
加入聊天室進行多人聊天
private MultiUserChat multiUserChat;
private void testMutiUserChat(XMPPConnection conn) throws Exception {
MultiUserChat.addInvitationListener(conn, new InvitationListener() {
@Override
public void invitationReceived(XMPPConnection conn, String room, String inviter, String reason, String password, Message message) {
StringBuilder sb = new StringBuilder();
sb.append("房間號 : ").append(room);
sb.append("\n邀請者 : ").append(inviter);
sb.append("\n理由 : ").append(reason);
sb.append("\n密碼 : ").append(password);
sb.append("\n消息 : ").append(message);
System.out.println(sb);
multiUserChat = new MultiUserChat(conn, room);
try {
multiUserChat.join("admin", password);
} catch (XMPPErrorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SmackException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
multiUserChat.addMessageListener(new PacketListener() {
@Override
public void processPacket(Packet packet) throws NotConnectedException {
Message msg = (Message) packet;
System.out.println(msg.getBody());
}
});
}
});
while(true) {
try {
Thread.sleep(500);
if(multiUserChat == null)
continue;
//關於發送消息的問題,可以直接發字符串
//也可以發送message,但是要設定message的一些參數,否則不能發送(參數設置如下)
//用Chat發送消息時,不用設置,原因是在Chat的sendMessage方法中已經添加了這些參數
/*
* message.setTo(participant);
message.setType(Message.Type.chat);
message.setThread(threadID);
*/
//但是,用MultiUserChat類中的sendMessage方法,直接調用了XMPPConnection中的sendPacket方法,沒有設置Message的參數
Message msg = new Message();
//房間名稱
msg.setTo("[email protected]");
msg.setType(Message.Type.groupchat);
msg.setThread(Thread.currentThread().getId() + "");
msg.setBody("hello");
multiUserChat.sendMessage(msg);
break;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (NotConnectedException e) {
e.printStackTrace();
} catch (XMPPException e) {
e.printStackTrace();
}
}
}
發送和接收文件
private void testSendFile(XMPPConnection conn) throws Exception {
// 發送文件的管理器
FileTransferManager ftm = new FileTransferManager(conn);
ftm.addFileTransferListener(new FileTransferListener() {
@Override
public void fileTransferRequest(FileTransferRequest request) {
System.out.println(request.getFileName());
IncomingFileTransfer inComingFileTransfer = request.accept();
try {
//可以直接寫到file文件中
File file = new File("D://" + request.getFileName());
inComingFileTransfer.recieveFile(file);
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 注意jid格式,下面爲標準格式,如果不對則會拋出jid格式錯誤的異常
// (if (parseName(jid).length() <= 0 || parseServer(jid).length() <= 0|| parseResource(jid).length() <= 0) {
// return false;
OutgoingFileTransfer oft = ftm.createOutgoingFileTransfer("[email protected]/潘迪安");
File file = new File("D://time.jpg");
oft.sendFile(file, "圖片");
System.out.println(oft.isDone());
}
創建多人聊天室
private void testCreateRoom(XMPPConnection conn) throws Exception {
while(true) {
if(conn != null)
break;
}
//@之前的是會議房間名稱,之後的是conference+ip(固定格式,不能改變)
MultiUserChat muc = new MultiUserChat(conn, "[email protected]");
//暱稱,如果該房間已經存在,則會拋出Creation failed - Missing acknowledge of room creation.(先加入房間,然後離開房間)
muc.create("real_admin");
Form form = muc.getConfigurationForm();
Form submitForm = form.createAnswerForm();
//下面的初始化有什麼用,在創建submitForm的時候已經設置參數了
// List<FormField> list = submitForm.getFields();
// for(FormField f : list) {
// if(!(FormField.TYPE_HIDDEN.equals(f.getType())) && f.getVariable() != null) {
// submitForm.setDefaultAnswer(f.getVariable());
// }
// }
//參數到底是什麼意思,爲什麼有的可以設置,有的不可以設置
/*
* variable:FORM_TYPE type:hidden value:[http://jabber.org/protocol/muc#roomconfig]
variable:muc#roomconfig_roomname type:text-single value:[]
variable:muc#roomconfig_roomdesc type:text-single value:[]
variable:muc#roomconfig_changesubject type:boolean value:[]
variable:muc#roomconfig_maxusers type:list-single value:[]
variable:muc#roomconfig_presencebroadcast type:list-multi value:[]
variable:muc#roomconfig_publicroom type:boolean value:[]
variable:muc#roomconfig_persistentroom type:boolean value:[]
variable:muc#roomconfig_moderatedroom type:boolean value:[]
variable:muc#roomconfig_membersonly type:boolean value:[]
variable:muc#roomconfig_allowinvites type:boolean value:[]
variable:muc#roomconfig_passwordprotectedroom type:boolean value:[]
variable:muc#roomconfig_roomsecret type:text-private value:[]
variable:muc#roomconfig_whois type:list-single value:[]
variable:muc#roomconfig_enablelogging type:boolean value:[]
variable:x-muc#roomconfig_reservednick type:boolean value:[]
variable:x-muc#roomconfig_canchangenick type:boolean value:[]
variable:x-muc#roomconfig_registration type:boolean value:[]
variable:muc#roomconfig_roomadmins type:jid-multi value:[]
variable:muc#roomconfig_roomowners type:jid-multi value:[]
*/
//submitForm.setAnswer(FormField.TYPE_TEXT_PRIVATE, "0");
muc.sendConfigurationForm(submitForm);
//被拒絕時執行
muc.addInvitationRejectionListener(new InvitationRejectionListener() {
@Override
public void invitationDeclined(String invitee, String reason) {
System.out.println(invitee + " : " + reason);
}
});
muc.invite("[email protected]", "ly_room");
}
管理房間
<pre name="code" class="java">private void testManageRoom(XMPPConnection conn) throws Exception {
testCreateRoom(conn);
MultiUserChat muc = new MultiUserChat(conn, "[email protected]");
//Thread.sleep(5000);
//賦予管理員權限
//muc.grantAdmin("[email protected]");
//Thread.sleep(5000);
//如果是管理員,則不能踢除
//muc.banUser("[email protected]", "太水");
//收回說話的權限
muc.revokeVoice("yy");
//muc.grantVoice("yy");
}
註冊
private void testRegister(XMPPConnection conn) throws Exception {
//可以直接改登陸用戶的信息(如果是username的值必須和該用戶的用戶名相同)
Registration r = new Registration();
Map<String, String> attributes = new HashMap<String, String>();
attributes.put("username", "newuser");
attributes.put("password", "0");
attributes.put("email", "[email protected]");
attributes.put("name", "[email protected]");
//添加用戶,要設置type類型爲set,原因不明
r.setType(IQ.Type.SET);
r.setAttributes(attributes);
//過濾器,用來過濾由服務器返回的信息(即得到註冊信息的內容)
PacketFilter packetFilter = new AndFilter(new PacketIDFilter(r.getPacketID()), new PacketTypeFilter(IQ.class));
PacketCollector collector = conn.createPacketCollector(packetFilter);
System.out.println(r);
conn.sendPacket(r);
IQ result = (IQ) collector.nextResult();
if(result == null) {
System.out.println("服務器沒有返回任何信息");
} else {
switch (result.getType().toString()) {
case "result":
System.out.println("註冊成功");
break;
case "error":
if(result.getError().toString().equalsIgnoreCase("conflict"))
System.out.println("用戶名稱已存在");
else
System.out.println("註冊失敗");
break;
default:
break;
}
}
}
管理賬號密碼
private void testModifyPwd(XMPPConnection conn) throws Exception {
//創建一個用戶信息管理,可以創建新用戶,或者修改用戶密碼
AccountManager am = AccountManager.getInstance(conn);
Collection<String> c = am.getAccountAttributes();
for(String s : c) {
System.out.println(s);
}
/*
* 通過accountManager可以得到的屬性
* username
email
registered
name
password
*/
am.getAccountAttribute("username");
am.createAccount("newUser", "0");
am.changePassword("00");
}
至於細節和中間遇到的問題,在程序代碼中都有敘述
參考博客:
http://blog.csdn.net/shimiso/article/details/11225873