基于xmpp openfire smack开发之smack类库介绍和使用[2]
关于Smack编程库,前面我们提到,它是面向Java端的api,主要在PC上使用,利用它我们可以向openfire服
务器注册用户,发送消息,并且可以通过监听器获得此用户的应答消息,以及构建聊天室,分组,个人通讯录等
等。
下面我们写几个程序小例子测试一下。
(1)登录操作
[java] view plaincopy
1. PPConnection.DEBUG_ENABLED = true;
2. AccountManager accountManager;
3. final ConnectionConfiguration connectionConfig = new ConnectionConfiguration(
4. "192.168.1.78", Integer.parseInt("5222"), "csdn.shimiso.com");
5.
6. // 允许自动连接
7. connectionConfig.setReconnectionAllowed(true);
8. connectionConfig.setSendPresence(true);
9.
10. Connection connection = new XMPPConnection(connectionConfig);
11. try {
12. connection.connect();// 开启连接
13. accountManager = connection.getAccountManager();// 获取账户管理类
14. } catch (XMPPException e) {
15. throw new IllegalStateException(e);
16. }
17.
18. // 登录
19. connection.login("admin", "admin","SmackTest");
20. System.out.println(connection.getUser());
21. connection.getChatManager().createChat("shimiso@csdn.shimiso.com",null).sendMessage("Hello word!")
;
运行结果:
在login中一共有三个参数,登录名,密码,资源名,可能有人不明白资源名到底是什么意思,其实就是客户端的来源,客户端的名称,如果不写它默认就叫smack,如果你用相同的账户不同的资源名和同一个人发三条消息,那将会弹出三个窗口,而不是一个窗口。
同时smack还为我们提供了非常好的调试工具Smack Debug,利用该工具我们可以准确的捕获详细的往返报文信
息。
(2)下面我们继续写个聊天的例子:
[java] view plaincopy
1. PPConnection.DEBUG_ENABLED = true;
2. AccountManager accountManager;
3. final ConnectionConfiguration connectionConfig = new ConnectionConfiguration(
4. "192.168.1.78", Integer.parseInt("5222"), "csdn.shimiso.com"); 5.
6. // 允许自动连接
7. connectionConfig.setReconnectionAllowed(true);
8. connectionConfig.setSendPresence(true);
9.
10. Connection connection = new XMPPConnection(connectionConfig); 11. try {
12. connection.connect();// 开启连接
13. accountManager = connection.getAccountManager();// 获取账户管理类 14. } catch (XMPPException e) {
15. throw new IllegalStateException(e);
16. }
17.
18. // 登录
19. connection.login("admin", "admin","SmackTest3"); 20. ChatManager chatmanager = connection.getChatManager(); 21. Chat newChat = chatmanager.createChat("shimiso@csdn.shimiso.com", new MessageListener() {
22. public void processMessage(Chat chat, Message message) { 23. if (message.getBody() != null) {
24. System.out.println("Received from 【" 25. + message.getFrom() + "】 message: " 26. + message.getBody());
27. }
28.
29. }
30. });
31. Scanner input = new Scanner(System.in);
32. while (true) {
33. String message = input.nextLine();
34. newChat.sendMessage(message);
35. }
运行结果:
这里我们用Scanner来捕捉用户在控制台的键盘操作,将信息发出,同时创建了一个MessageListener监听,在其中强制实现processMessage方法即可捕获发回的信息,在初次使用上还是较为容易上手的,我们只要细心查看API即可逐步深入下去。
(3)除了聊天以外我们经常还能想到就是广播
需要给所有在线的用户发送一个
通知
关于发布提成方案的通知关于xx通知关于成立公司筹建组的通知关于红头文件的使用公开通知关于计发全勤奖的通知
,或者给所有在线和离线的用户全发送,我们先演示如何给在线用户发送一个广播:
[java] view plaincopy
1. PPConnection.DEBUG_ENABLED = false;
2. AccountManager accountManager;
3. final ConnectionConfiguration connectionConfig = new ConnectionConfiguration(
4. "192.168.1.78", Integer.parseInt("5222"), "csdn.shimiso.com");
5.
6. // 允许自动连接
7. connectionConfig.setReconnectionAllowed(true);
8. connectionConfig.setSendPresence(true);
9.
10. Connection connection = new XMPPConnection(connectionConfig);
11. try {
12. connection.connect();// 开启连接
13. accountManager = connection.getAccountManager();// 获取账户管理类
14. } catch (XMPPException e) {
15. throw new IllegalStateException(e);
16. }
17. connection.login("admin", "admin","SmackTest3");
18. Message newmsg = new Message();
19. newmsg.setTo("shimiso@csdn.shimiso.com");
20. newmsg.setSubject("重要通知");
21. newmsg.setBody("今天下午2点60分有会~");
22. newmsg.setType(Message.Type.headline);// normal支持离线
23. connection.sendPacket(newmsg);
24. connection.disconnect();
运行结果:
将参数设置为Message.Type.normal即可支持离线广播,openfire系统会自动判断该用户是否在线,如果在线就直
接发送出去,如果不在线则将信息存入ofoffline
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
,现在我将shimiso用户退出登录,再给它发消息,我们可以
进入openfire库的ofoffline表中,非常清楚看到里面躺着一条离线消息记录是发给shimiso这个用户的
(4)那么我们如何让shimiso这个用户一登陆就取到离线消息呢,
请看如下代码
[java] view plaincopy
1. PPConnection.DEBUG_ENABLED = false;
2. AccountManager accountManager;
3. final ConnectionConfiguration connectionConfig = new ConnectionConfiguration( 4. "192.168.1.78", Integer.parseInt("5222"), "csdn.shimiso.com"); 5.
6. // 允许自动连接
7. connectionConfig.setReconnectionAllowed(true);
8. connectionConfig.setSendPresence(false);//不要告诉服务器自己的状态 9. Connection connection = new XMPPConnection(connectionConfig); 10. try {
11. connection.connect();// 开启连接
12. accountManager = connection.getAccountManager();// 获取账户管理类 13. } catch (XMPPException e) {
14. throw new IllegalStateException(e);
15. }
16. connection.login("shimiso", "123","SmackTest");
17. OfflineMessageManager offlineManager = new OfflineMessageManager( 18. connection);
19. try {
20. Iterator
it = offlineManager 21. .getMessages();
22.
23. System.out.println(offlineManager.supportsFlexibleRetrieval()); 24. System.out.println("离线消息数量: " + offlineManager.getMessageCount());
25.
26. Map> offlineMsgs = new HashMap>();
27.
28. while (it.hasNext()) {
29. org.jivesoftware.smack.packet.Message message = it.next(); 30. System.out
31. .println("收到离线消息, Received from 【" + message.getFrom()
32. + "】 message: " + message.getBody()); 33. String fromUser = message.getFrom().split("/")[0]; 34.
35. if (offlineMsgs.containsKey(fromUser)) { 36. offlineMsgs.get(fromUser).add(message); 37. } else {
38. ArrayList temp = new ArrayList(); 39. temp.add(message);
40. offlineMsgs.put(fromUser, temp);
41. }
42. }
43.
44. // 在这里进行处理离线消息集合......
45. Set keys = offlineMsgs.keySet();
46. Iterator offIt = keys.iterator(); 47. while (offIt.hasNext()) {
48. String key = offIt.next();
49. ArrayList ms = offlineMsgs.get(key); 50.
51. for (int i = 0; i < ms.size(); i++) { 52. System.out.println("-->" + ms.get(i)); 53. }
54. }
55.
56. offlineManager.deleteMessages();
57. } catch (Exception e) {
58. e.printStackTrace();
59. }
60. offlineManager.deleteMessages();//删除所有离线消息 61. Presence presence = new Presence(Presence.Type.available); 62. nnection.sendPacket(presence);//上线了 63. nnection.disconnect();//关闭连接
运行结果:
这里我们需要特别当心的是先不要告诉openfire服务器你上线了,否则永远也拿不到离线消息,用下面英文大概意思就是在你上线之前去获取离线消息,这么设计是很有道理的。
The OfflineMessageManager helps manage offline messages even before the user has sent an available presence. When a
user asks for his offline messages before sending an available presence then the server will not send a flood with all the
offline messages when the user becomes online. The server will not send a flood with all the offline messages to the
session that made the offline messages request or to any other session used by the user that becomes online. 拿到离线消息处理完毕之后删除离线消息offlineManager.deleteMessages() 接着通知服务器上线了。 (5)下面我们来看看如何来发送文件
[java] view plaincopy
1. PPConnection.DEBUG_ENABLED = false;
2. AccountManager accountManager;
3. final ConnectionConfiguration connectionConfig = new ConnectionConfiguration(
4. "192.168.1.78", Integer.parseInt("5222"), "csdn.shimiso.com");
5.
6. // 允许自动连接
7. connectionConfig.setReconnectionAllowed(true);
8. connectionConfig.setSendPresence(true);
9.
10. Connection connection = new XMPPConnection(connectionConfig);
11. try {
12. connection.connect();// 开启连接
13. accountManager = connection.getAccountManager();// 获取账户管理类 14. } catch (XMPPException e) {
15. throw new IllegalStateException(e);
16. }
17. connection.login("admin", "admin","Rooyee");
18. Presence pre = connection.getRoster().getPresence("shimiso@csdn.shimiso.com"); 19. System.out.println(pre);
20. if (pre.getType() != Presence.Type.unavailable) { 21. // 创建文件传输管理器
22. FileTransferManager manager = new FileTransferManager(connection); 23. // 创建输出的文件传输
24. OutgoingFileTransfer transfer = manager
25. .createOutgoingFileTransfer(pre.getFrom()); 26. // 发送文件
27. transfer.sendFile(new File("E:\\Chrysanthemum.jpg"), "图片"); 28. while (!transfer.isDone()) {
29. if (transfer.getStatus() == FileTransfer.Status.in_progress) { 30. // 可以调用transfer.getProgress();获得传输的进度 31. System.out.println(transfer.getStatus()); 32. System.out.println(transfer.getProgress()); 33. System.out.println(transfer.isDone()); 34. }
35. }
36. }
运行结果:
在这里我们需要特别注意的是,跨资源是无法发送文件的,看connection.login("admin", "admin","Rooyee");这个代码就明白了,必须是“域名和资源名”完全相同的两个用户才可以互发文件,否则永远都没反应,如果不清楚自己所用的客户端的资源名,可以借助前面提到的SmackDebug工具查看往返信息完整报文,在to和from中一定可以看到。
如果我们自己要写文件接收例子的话,参考代码如下:
[java] view plaincopy
1. FileTransferManager transfer = new FileTransferManager(connection);
2. transfer.addFileTransferListener(new RecFileTransferListener());
3. public class RecFileTransferListener implements FileTransferListener {
4.
5. public String getFileType(String fileFullName) {
6. if (fileFullName.contains(".")) {
7. return "." + fileFullName.split("//.")[1];
8. } else {
9. return fileFullName;
10. }
11.
12. }
13.
14. @Override
15. public void fileTransferRequest(FileTransferRequest request) { 16. System.out.println("接收文件开始.....");
17. final IncomingFileTransfer inTransfer = request.accept(); 18. final String fileName = request.getFileName(); 19. long length = request.getFileSize();
20. final String fromUser = request.getRequestor().split("/")[0]; 21. System.out.println("文件大小:" + length + " " + request.getRequestor()); 22. System.out.println("" + request.getMimeType()); 23. try {
24.
25. JFileChooser chooser = new JFileChooser(); 26. chooser.setCurrentDirectory(new File(".")); 27.
28. int result = chooser.showOpenDialog(null); 29.
30. if (result == JFileChooser.APPROVE_OPTION) { 31. final File file = chooser.getSelectedFile(); 32. System.out.println(file.getAbsolutePath()); 33. new Thread() {
34. public void run() {
35. try {
36.
37. System.out.println("接受文件: " + fileName); 38. inTransfer
39. .recieveFile(new File(file 40. .getAbsolutePath() 41. + getFileType(fileName))); 42.
43. Message message = new Message(); 44. message.setFrom(fromUser); 45. message.setProperty("REC_SIGN", "SUCCESS"); 46. message.setBody("[" + fromUser + "]发送文件: " 47. + fileName + "/r/n" + "存储位置: " 48. + file.getAbsolutePath() 49. + getFileType(fileName)); 50. if (Client.isChatExist(fromUser)) { 51. Client.getChatRoom(fromUser) 52. .messageReceiveHandler(message); 53. } else {
54. ChatFrameThread cft = new ChatFrameThread( 55. fromUser, message); 56. cft.start();
57.
58. }
59. } catch (Exception e2) { 60. e2.printStackTrace(); 61. }
62. }
63. }.start();
64. } else {
65.
66. System.out.println("拒绝接受文件: " + fileName); 67.
68. request.reject();
69. Message message = new Message(); 70. message.setFrom(fromUser);
71. message.setBody("拒绝" + fromUser + "发送文件: " + fileName);
72. message.setProperty("REC_SIGN", "REJECT"); 73. if (Client.isChatExist(fromUser)) { 74. Client.getChatRoom(fromUser).messageReceiveHandler(message);
75. } else {
76. ChatFrameThread cft = new ChatFrameThread(fromUser, message);
77. cft.start();
78. }
79. }
80.
81. /*
82. * InputStream in = inTransfer.recieveFile(); 83. *
84. * String fileName = "r"+inTransfer.getFileName(); 85. *
86. * OutputStream out = new FileOutputStream(new 87. * File("d:/receive/"+fileName)); byte[] b = new byte[512]; 88. * while(in.read(b) != -1) { out.write(b); out.flush(); } 89. *
90. * in.close(); out.close();
91. */
92. } catch (Exception e) {
93. e.printStackTrace();
94. }
95.
96. System.out.println("接收文件结束....."); 97.
98. }
99.
100. }
(6)用户列表
[java] view plaincopy
1. **
2. * 返回所有组信息
3. *
4. * @return List(RosterGroup)
5. */
6. public static List getGroups(Roster roster) { 7. List groupsList = new ArrayList(); 8. Collection rosterGroup = roster.getGroups(); 9. Iterator i = rosterGroup.iterator(); 10. while (i.hasNext())
11. groupsList.add(i.next());
12. return groupsList;
13. }
14.
15. /**
16. * 返回相应(groupName)组里的所有用户
17. *
18. * @return List(RosterEntry)
19. */
20. public static List getEntriesByGroup(Roster roster, 21. String groupName) {
22. List EntriesList = new ArrayList(); 23. RosterGroup rosterGroup = roster.getGroup(groupName); 24. Collection rosterEntry = rosterGroup.getEntries(); 25. Iterator i = rosterEntry.iterator(); 26. while (i.hasNext())
27. EntriesList.add(i.next());
28. return EntriesList;
29. }
30.
31. /**
32. * 返回所有用户信息
33. *
34. * @return List(RosterEntry)
35. */
36. public static List getAllEntries(Roster roster) { 37. List EntriesList = new ArrayList(); 38. Collection rosterEntry = roster.getEntries(); 39. Iterator i = rosterEntry.iterator(); 40. while (i.hasNext())
41. EntriesList.add(i.next());
42. return EntriesList;
43. }
(7)用户头像的获取
使用VCard,很强大,具体自己看API吧,可以看看VCard传回来XML的组成,含有很多信息的
[java] view plaincopy
1. **
2. * 获取用户的vcard信息
3. * @param connection
4. * @param user
5. * @return
6. * @throws XMPPException
7. */
8. public static VCard getUserVCard(XMPPConnection connection, String user) throws XMPPException
9. {
10. VCard vcard = new VCard();
11. vcard.load(connection, user);
12.
13. return vcard;
14. }
15.
16. /**
17. * 获取用户头像信息
18. */
19. public static ImageIcon getUserImage(XMPPConnection connection, String user) {
20. ImageIcon ic = null;
21. try {
22. System.out.println("获取用户头像信息: "+user); 23. VCard vcard = new VCard();
24. vcard.load(connection, user); 25.
26. if(vcard == null || vcard.getAvatar() == null) 27. {
28. return null;
29. }
30. ByteArrayInputStream bais = new ByteArrayInputStream(
31. vcard.getAvatar());
32. Image image = ImageIO.read(bais); 33.
34.
35. ic = new ImageIcon(image);
36. System.out.println("图片大小:"+ic.getIconHeight()+" "+ic.getIconWidth());
37.
38. } catch (Exception e) { 39. e.printStackTrace(); 40. }
41. return ic;
42. }
(8)组操作和用户分组操作
[java] view plaincopy
1. **
2. * 添加一个组
3. */
4. public static boolean addGroup(Roster roster,String groupName)
5. {
6. try {
7. roster.createGroup(groupName);
8. return true;
9. } catch (Exception e) { 10. e.printStackTrace(); 11. return false; 12. }
13. }
14.
15. /**
16. * 删除一个组
17. */
18. public static boolean removeGroup(Roster roster,String groupName)
19. {
20. return false;
21. }
22.
23. /**
24. * 添加一个好友 无分组
25. */
26. public static boolean addUser(Roster roster,String userName,String name)
27. {
28. try {
29. roster.createEntry(userName, name, null);
30. return true;
31. } catch (Exception e) { 32. e.printStackTrace(); 33. return false; 34. }
35. }
36. /**
37. * 添加一个好友到分组
38. * @param roster
39. * @param userName
40. * @param name
41. * @return
42. */
43. public static boolean addUser(Roster roster,String userName,String name,String groupName)
44. {
45. try {
46. roster.createEntry(userName, name,new String[]{ groupName});
47. return true;
48. } catch (Exception e) { 49. e.printStackTrace(); 50. return false;
51. }
52. }
53.
54. /**
55. * 删除一个好友
56. * @param roster
57. * @param userName
58. * @return
59. */
60. public static boolean removeUser(Roster roster,String userName)
61. {
62. try {
63.
64. if(userName.contains("@")) 65. {
66. userName = userName.split("@")[0];
67. }
68. RosterEntry entry = roster.getEntry(userName);
69. System.out.println("删除好友:"+userName);
70. System.out.println("User: "+(roster.getEntry(userName) == null));
71. roster.removeEntry(entry); 72.
73. return true;
74. } catch (Exception e) { 75. e.printStackTrace(); 76. return false;
77. }
78.
79. }
(9)用户查询
[java] view plaincopy
1. public static List searchUsers(XMPPConnection connection,String serverDomain,String user
Name) throws XMPPException
2. {
3. List results = new ArrayList(); 4. System.out.println("查询开
始..............."+connection.getHost()+connection.getServiceName()); 5.
6. UserSearchManager usm = new UserSearchManager(connection); 7.
8.
9. Form searchForm = usm.getSearchForm(serverDomain); 10. Form answerForm = searchForm.createAnswerForm(); 11. answerForm.setAnswer("Username", true);
12. answerForm.setAnswer("search", userName);
13. ReportedData data = usm.getSearchResults(answerForm, serverDomain); 14.
15. Iterator it = data.getRows();
16. Row row = null;
17. UserBean user = null;
18. while(it.hasNext())
19. {
20. user = new UserBean();
21. row = it.next();
22. user.setUserName(row.getValues("Username").next().toString()); 23. user.setName(row.getValues("Name").next().toString()); 24. user.setEmail(row.getValues("Email").next().toString()); 25. System.out.println(row.getValues("Username").next()); 26. System.out.println(row.getValues("Name").next()); 27. System.out.println(row.getValues("Email").next()); 28. results.add(user);
29. //若存在,则有返回,UserName一定非空,其他两个若是有设,一定非空 30. }
31.
32. return results;
33. }
(10)修改自身状态
包括上线,隐身,对某人隐身,对某人上线
[java] view plaincopy
1. ublic static void updateStateToAvailable(XMPPConnection connection) 2. {
3. Presence presence = new Presence(Presence.Type.available); 4. nnection.sendPacket(presence);
5.
6.
7. public static void updateStateToUnAvailable(XMPPConnection connection) 8. {
9. Presence presence = new Presence(Presence.Type.unavailable); 10. nnection.sendPacket(presence);
11. }
12.
13. public static void updateStateToUnAvailableToSomeone(XMPPConnection connection,String userName)
14. {
15. Presence presence = new Presence(Presence.Type.unavailable); 16. presence.setTo(userName);
17. nnection.sendPacket(presence);
18. }
19. public static void updateStateToAvailableToSomeone(XMPPConnection connection,String userName)
20. {
21. Presence presence = new Presence(Presence.Type.available); 22. presence.setTo(userName);
23. nnection.sendPacket(presence);
24.
25. }
(11)心情修改
[java] view plaincopy
1. **
2. * 修改心情
3. * @param connection
4. * @param status
5. */
6. public static void changeStateMessage(XMPPConnection connection,String status) 7. {
8. Presence presence = new Presence(Presence.Type.available); 9. presence.setStatus(status);
10. connection.sendPacket(presence);
11.
12. }
(12)修改用户头像
有点麻烦,主要是读入图片文件,编码,传输之
[java] view plaincopy
1. public static void changeImage(XMPPConnection connection,File f) throws XMPPException, IOException
{
2.
3. VCard vcard = new VCard();
4. vcard.load(connection);
5.
6. byte[] bytes;
7.
8. bytes = getFileBytes(f);
9. String encodedImage = StringUtils.encodeBase64(bytes); 10. vcard.setAvatar(bytes, encodedImage); 11. vcard.setEncodedImage(encodedImage); 12. vcard.setField("PHOTO", "image/jpg" 13. + encodedImage + "", true); 14.
15.
16. ByteArrayInputStream bais = new ByteArrayInputStream( 17. vcard.getAvatar());
18. Image image = ImageIO.read(bais); 19. ImageIcon ic = new ImageIcon(image); 20.
21.
22.
23. vcard.save(connection);
24.
25. }
26.
27. private static byte[] getFileBytes(File file) throws IOException { 28. BufferedInputStream bis = null; 29. try {
30. bis = new BufferedInputStream(new FileInputStream(file)); 31. int bytes = (int) file.length();
32. byte[] buffer = new byte[bytes];
33. int readBytes = bis.read(buffer);
34. if (readBytes != buffer.length) {
35. throw new IOException("Entire file not read"); 36. }
37. return buffer;
38. } finally {
39. if (bis != null) {
40. bis.close();
41. }
42. }
43. }
(13)用户状态的监听
即对方改变头像,状态,心情时,更新自己用户列表,其实这里已经有smack实现的监听器
[java] view plaincopy
1. nal Roster roster = Client.getRoster();
2.
3. roster.addRosterListener(
4. new RosterListener() {
5.
6. @Override
7. public void entriesAdded(Collection arg0) { 8. // TODO Auto-generated method stub 9. System.out.println("--------EE:"+"entriesAdded"); 10. }
11.
12. @Override
13. public void entriesDeleted(Collection arg0) { 14. // TODO Auto-generated method stub 15. System.out.println("--------EE:"+"entriesDeleted"); 16. }
17.
18. @Override
19. public void entriesUpdated(Collection arg0) { 20. // TODO Auto-generated method stub 21. System.out.println("--------EE:"+"entriesUpdated"); 22. }
23.
24. @Override
25. public void presenceChanged(Presence arg0) { 26. // TODO Auto-generated method stub 27. System.out.println("--------EE:"+"presenceChanged"); 28. }
29.
30. });