利用openfire和smark的即時通信

服務器: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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章