openKylin论坛

 找回密码

xmpp smack类库实现聊天功能 [复制链接]

[1]连接、登陆及账户操作需求:
基于XMPP的IM工具,需实现和gtalk实现通信,需实现同spark的通信,需架设服务器,实现同自身客户端的通信,传文件,视频聊天

写完未实现需要改进的地方:离线消息,离线文件
一、XMPP
XMPP : The Extensible Messaging and Presence Protocol
中文全称:可扩展通讯和表示协议
简介:
可扩展通讯和表示协议 (XMPP) 可用于服务类实时通讯、表示和需求 - 响应服务中的 XML 数据元流式传输。XMPP 以 Jabber 协议为基础,而 Jabber 是即时通讯中常用的开放式协议。

二、Smack
    Smack是一个开源,易于使用的XMPP(jabber)客户端类库。
   Smack API, 是一个 Java 的XMPP Client Library,也是由Jive Software开发。 优点:编程简单。 缺点:API并非为大量并发用户设计,每个客户要1个线程,占用资源大,1台机器只能模拟有限(数千个)客户.
smack是一个用 java 写的XMPP客户端代码库, 是 spark 的核心.

二、连接及断开
基本方法
XMPPConnection connection = new XMPPConnection("gmail.com");
        connection.connect();
实现的方法

[java] view plaincopyprint?

  • public static XMPPConnection getConnection(String domain) throws XMPPException {
  •      XMPPConnection connection = new XMPPConnection(domain);
  •      connection.connect();
  •      return connection;
  • }
  • public static XMPPConnection getConnection(String domain,int port) throws XMPPException {
  •      ConnectionConfiguration config = new ConnectionConfiguration(domain,port);
  •      XMPPConnection connection = new XMPPConnection(config);
  •      connection.connect();
  •      return connection;
  • }

断开   
    connection.disconnect();
      
四、登陆
connection.login("javatest2011@gmail.com", "*****");

五、账户操作

   可以对账户进行基本操作,包括注册,注销,修改密码
[c-sharp] view plaincopyprint?

  • /**
  •      * 注册用户
  •      * @param connection
  •      * @param regUserName
  •      * @param regUserPwd
  •      * @return
  •      */
  •     public static boolean createAccount(XMPPConnection connection,String regUserName,String regUserPwd)
  •     {
  •         try {
  •             connection.getAccountManager().createAccount(regUserName, regUserPwd);
  •             return true;
  •         } catch (Exception e) {
  •             return false;
  •         }
  •     }
  •     /**
  •      * 删除当前用户
  •      * @param connection
  •      * @return
  •      */
  •     public static boolean deleteAccount(XMPPConnection connection)
  •     {
  •         try {
  •             connection.getAccountManager().deleteAccount();
  •             return true;
  •         } catch (Exception e) {
  •             return false;
  •         }
  •     }
  •     /**
  •      * 删除修改密码
  •      * @param connection
  •      * @return
  •      */
  •     public static boolean changePassword(XMPPConnection connection,String pwd)
  •     {
  •         try {
  •             connection.getAccountManager().changePassword(pwd);
  •             return true;
  •         } catch (Exception e) {
  •             return false;
  •         }
  •     }


[2]会话、消息监听、字体表情和聊天窗口控制上一篇是连接,登陆登出和账户管理
继续
连接之后,拿到了connection,通过它可以搞定会话


1.       建立一个会话
[java] view plaincopyprint?

  • MessageListener msgListener
  • = new MessageListener()
  •         {
  •             public void processMessage(Chat chat, Message message)
  •             {
  •                 if (message != null && message.getBody() != null)
  •                 {
  •                     System.out.println("收到消息:" + message.getBody());
  •                     // 可以在这进行针对这个用户消息的处理,但是这里我没做操作,看后边聊天窗口的控制
  •                 }
  •             }
  •         };
  • Chat chat = Client.getConnection().getChatManager()
  •                 .createChat(userName, msgListener);


通过会话发送消息
两个方法,一个直接发送一条文本,一个发送一个Message对象,可包含一些信息,一般使用后者,因为需要包装字体等信息
[java] view plaincopyprint?

  • public static void sendMessage(Chat chat,String message) throws XMPPException {
  •         chat.sendMessage(message);
  •     }
  •     public static void sendMessage(Chat chat,Message message) throws XMPPException {
  •         chat.sendMessage(message);
  •     }



2.       消息监听
每个connection的chatManager可以设置一个消息监听器,因为IM必须实现他人对你发起会话也会弹出窗口,即自己可以主动发起会话,也可以接收他人发起的会话
[java] view plaincopyprint?

  • ChatManager manager = Client.getConnection().getChatManager();
  •         manager.addChatListener(new ChatManagerListener() {
  •             public void chatCreated(Chat chat, boolean arg1) {
  •                 chat.addMessageListener(new MessageListener() {
  •                     public void processMessage(Chat arg0, Message message) {
  •                      //若是聊天窗口已存在,将消息转往目前窗口
  •                      //若是窗口不存在,开新的窗口并注册
  •                     }
  •                 });
  •             }
  •         });

   其实窗口的管理是使用线程的,来一个新的会话,起线程
3.  字体表情
在这里实现字体和表情是使用自身开发IM之间的实现。

字体实现思想:
在发送消息的同时,将字体内容作为附加信息发送,接收方接收到根据字体信息进行处理后显示
   
   实现:使用Message对消息进行封装
[java] view plaincopyprint?

  • Message msg = new Message();
  •         msg.setProperty("size", size);
  •         msg.setProperty("kind", kind);
  •         msg.setProperty("bold", bold);
  •         msg.setProperty("italic", italic);
  •         msg.setProperty("underline", underline);
  •         msg.setProperty("color", color);
  •         msg.setBody(getSendInfo());//真正的消息
  •         chat.sendMessage(msg);



接收方先获取设置信息后展示
       展示的控件: JTextPanereceiveText =new JTextPane();
[java] view plaincopyprint?

  • Style style = receiveText.addStyle("font", null);
  •         StyleConstants.setFontSize(style, size);
  •         StyleConstants.setFontFamily(style, kind);
  •         StyleConstants.setBold(style, bold);
  •         StyleConstants.setItalic(style, italic);
  •         StyleConstants.setUnderline(style, underline);
  •         StyleConstants.setForeground(style, color);


  表情:
       实现机制是客户端本身存有一套表情图片,在选中时,将表情编号加入到消息中,实际发送出去的只是文本,拿到后解析字符串,将编号转为具体表情展示
      具体就不写了

1.  聊天窗口控制
所谓控制,主要是控制唯一性
无论是你发起一个会话开启一个窗口,还是对方给你发送会话开启,你与对方之间有且仅有一个窗口,之后任何消息都在这个窗口中处理
思想:单例类,持有一个
//现有的聊天窗口
    publicstatic TreeMap<String,TelFrame>currentChat =new TreeMap<String,TelFrame>();
其实应该用concurrentHashMap可能会更好,不顾我这边分派的主线程只有一个,不涉及多个线程并发的问题,所以用了Treemap,汗一个,貌似应该hashmap,当时考虑不同
然后,在接收消息的时候根据发消息的用户,判定窗口是否存在,存在,转发消息到该窗口,不存在,建立新的窗口
若是关闭窗口,注销之

[java] view plaincopyprint?

  • //注册聊天室
  •     public static void registerChat(String userName,TelFrame chatroom)
  •     {
  •         //System.out.println("注册:"+userName);
  •         currentChat.put(userName, chatroom);
  •     }
  •     //注销注册
  •     public static void removeChat(String userName)
  •     {
  •         System.out.println("用户注销聊天室:"+userName);
  •         currentChat.remove(userName);
  •     }
  •     //查看是否已有
  •     public static boolean isChatExist(String userName)
  •     {
  •         return currentChat.containsKey(userName);
  •     }
  •     //获取对象
  •     public static TelFrame getChatRoom(String userName)
  •     {
  •         return currentChat.get(userName);
  •     }
  • 下一篇,主要是用户列表,头像,分组及管理的

[3]用户列表,头像,组操作,用户操作转载请注明出处:http://blog.csdn.net/wklken/archive/2011/06/01/6460112.aspx
上一篇主要是会话的管理
继续,这是显示用户列表方面的

1.       用户列表
Smack主要使用Roster进行列表管理的
connection.getRoster();
[java] view plaincopyprint?

  • /**
  •      * 返回所有组信息 <RosterGroup>
  •      *
  •      * @return List(RosterGroup)
  •      */
  •     public static List<RosterGroup> getGroups(Roster roster) {
  •         List<RosterGroup> groupsList = new ArrayList<RosterGroup>();
  •         Collection<RosterGroup> rosterGroup = roster.getGroups();
  •         Iterator<RosterGroup> i = rosterGroup.iterator();
  •         while (i.hasNext())
  •             groupsList.add(i.next());
  •         return groupsList;
  •     }
  •     /**
  •      * 返回相应(groupName)组里的所有用户<RosterEntry>
  •      *
  •      * @return List(RosterEntry)
  •      */
  •     public static List<RosterEntry> getEntriesByGroup(Roster roster,
  •             String groupName) {
  •         List<RosterEntry> EntriesList = new ArrayList<RosterEntry>();
  •         RosterGroup rosterGroup = roster.getGroup(groupName);
  •         Collection<RosterEntry> rosterEntry = rosterGroup.getEntries();
  •         Iterator<RosterEntry> i = rosterEntry.iterator();
  •         while (i.hasNext())
  •             EntriesList.add(i.next());
  •         return EntriesList;
  •     }
  •     /**
  •      * 返回所有用户信息 <RosterEntry>
  •      *
  •      * @return List(RosterEntry)
  •      */
  •     public static List<RosterEntry> getAllEntries(Roster roster) {
  •         List<RosterEntry> EntriesList = new ArrayList<RosterEntry>();
  •         Collection<RosterEntry> rosterEntry = roster.getEntries();
  •         Iterator<RosterEntry> i = rosterEntry.iterator();
  •         while (i.hasNext())
  •             EntriesList.add(i.next());
  •         return EntriesList;
  •     }


这里注意下,与gtalk通讯,貌似gtalk是没有分组的,汗,所以使用第三个方法直接取

当然,还要处理,若是刚注册用户,一个组都没有的,需要默认两个组,我的好友及黑名单
黑名单的消息,一律杀掉,不会接受处理
2.       用户头像的获取
使用VCard,很强大,具体自己看API吧
可以看看VCard传回来XML的组成,含有很多信息的

[java] view plaincopyprint?

  • /**
  •      * 获取用户的vcard信息
  •      * @param connection
  •      * @param user
  •      * @return
  •      * @throws XMPPException
  •      */
  •     public static VCard getUserVCard(XMPPConnection connection, String user) throws XMPPException
  •     {
  •         VCard vcard = new VCard();
  •         vcard.load(connection, user);
  •         return vcard;
  •     }
  • 获取头像使用
  •     /**
  •      * 获取用户头像信息
  •      */
  •     public static ImageIcon getUserImage(XMPPConnection connection, String user) {
  •         ImageIcon ic = null;
  •         try {
  •             System.out.println("获取用户头像信息: "+user);
  •             VCard vcard = new VCard();
  •             vcard.load(connection, user);
  •             if(vcard == null || vcard.getAvatar() == null)
  •             {
  •                 return null;
  •             }
  •             ByteArrayInputStream bais = new ByteArrayInputStream(
  •                     vcard.getAvatar());
  •             Image image = ImageIO.read(bais);
  •             ic = new ImageIcon(image);
  •             System.out.println("图片大小:"+ic.getIconHeight()+" "+ic.getIconWidth());
  •         } catch (Exception e) {
  •             e.printStackTrace();
  •         }
  •         return ic;
  •     }

  
3.       组操作和用户分组操作
主要是建立删除分组,用户添加到分组等操作


[c-sharp] view plaincopyprint?

  • /**
  •      * 添加一个组
  •      */
  •     public static boolean addGroup(Roster roster,String groupName)
  •     {
  •         try {
  •             roster.createGroup(groupName);
  •             return true;
  •         } catch (Exception e) {
  •             e.printStackTrace();
  •             return false;
  •         }
  •     }
  •     /**
  •      * 删除一个组
  •      */
  •     public static boolean removeGroup(Roster roster,String groupName)
  •     {
  •         return false;
  •     }
  •     /**
  •      * 添加一个好友  无分组
  •      */
  •     public static boolean addUser(Roster roster,String userName,String name)
  •     {
  •         try {
  •             roster.createEntry(userName, name, null);
  •             return true;
  •         } catch (Exception e) {
  •             e.printStackTrace();
  •             return false;
  •         }
  •     }
  •     /**
  •      * 添加一个好友到分组
  •      * @param roster
  •      * @param userName
  •      * @param name
  •      * @return
  •      */
  •     public static boolean addUser(Roster roster,String userName,String name,String groupName)
  •     {
  •         try {
  •             roster.createEntry(userName, name,new String[]{ groupName});
  •             return true;
  •         } catch (Exception e) {
  •             e.printStackTrace();
  •             return false;
  •         }
  •     }
  •     /**
  •      * 删除一个好友
  •      * @param roster
  •      * @param userName
  •      * @return
  •      */
  •     public static boolean removeUser(Roster roster,String userName)
  •     {
  •         try {
  •             if(userName.contains("@"))
  •             {
  •                 userName = userName.split("@")[0];
  •             }
  •             RosterEntry entry = roster.getEntry(userName);
  •             System.out.println("删除好友:"+userName);
  •             System.out.println("User: "+(roster.getEntry(userName) == null));
  •             roster.removeEntry(entry);
  •             return true;
  •         } catch (Exception e) {
  •             e.printStackTrace();
  •             return false;
  •         }
  •     }

  
4.     用户查询
本来是用户操作的,分组和增删在3里讲了,这里主要是查询操作
查询用户
[java] view plaincopyprint?

  • public static List<UserBean> searchUsers(XMPPConnection connection,String serverDomain,String userName) throws XMPPException
  •     {
  •         List<UserBean> results = new ArrayList<UserBean>();
  •         System.out.println("查询开始..............."+connection.getHost()+connection.getServiceName());
  •         UserSearchManager usm = new UserSearchManager(connection);
  •         Form searchForm = usm.getSearchForm(serverDomain);
  •         Form answerForm = searchForm.createAnswerForm();
  •         answerForm.setAnswer("Username", true);
  •         answerForm.setAnswer("search", userName);
  •         ReportedData data = usm.getSearchResults(answerForm, serverDomain);
  •          Iterator<Row> it = data.getRows();
  •          Row row = null;
  •          UserBean user = null;
  •          while(it.hasNext())
  •          {
  •              user = new UserBean();
  •              row = it.next();
  •              user.setUserName(row.getValues("Username").next().toString());
  •              user.setName(row.getValues("Name").next().toString());
  •              user.setEmail(row.getValues("Email").next().toString());
  •              System.out.println(row.getValues("Username").next());
  •              System.out.println(row.getValues("Name").next());
  •              System.out.println(row.getValues("Email").next());
  •              results.add(user);
  •              //若存在,则有返回,UserName一定非空,其他两个若是有设,一定非空
  •          }
  •          return results;
  •     }


以上查询貌似是多字段查询,即用户多个属性中某一个符合即作为查询结果
实际是可以实现根据某一特定字段查询的,如用户名,或昵称,这里笼统了,若需扩展去查看下API重写下


下一篇,状态,心情和头像修改
[4]状态,心情,头像更改这里写完,最基本的IM功能也就完了,
还剩下个发送接收文件,离线消息扩展等等

呵呵,三天时间,看的不是很深入,欢迎大家补充呀

1.       修改自身状态
包括上线,隐身,对某人隐身,对某人上线
[java] view plaincopyprint?

  • public static void updateStateToAvailable(XMPPConnection connection)
  •     {
  •         Presence presence = new Presence(Presence.Type.available);
  •         connection.sendPacket(presence);
  •      }
  •     public static void updateStateToUnAvailable(XMPPConnection connection)
  •     {
  •         Presence presence = new Presence(Presence.Type.unavailable);
  •         connection.sendPacket(presence);
  •         }
  •     public static void updateStateToUnAvailableToSomeone(XMPPConnection connection,String userName)
  •     {
  •         Presence presence = new Presence(Presence.Type.unavailable);
  •         presence.setTo(userName);
  •         connection.sendPacket(presence);
  •     }
  •     public static void updateStateToAvailableToSomeone(XMPPConnection connection,String userName)
  •     {
  •         Presence presence = new Presence(Presence.Type.available);
  •         presence.setTo(userName);
  •         connection.sendPacket(presence);
  •     }

2.       心情修改

[java] view plaincopyprint?

  • /**
  •      * 修改心情
  •      * @param connection
  •      * @param status
  •      */
  •     public static void changeStateMessage(XMPPConnection connection,String status)
  •     {
  •         Presence presence = new Presence(Presence.Type.available);
  •         presence.setStatus(status);
  •         connection.sendPacket(presence);
  •     }

3.       修改用户头像
有点麻烦,主要是读入图片文件,编码,传输之
[java] view plaincopyprint?

  • public static void changeImage(XMPPConnection connection,File f) throws XMPPException, IOException
  •     {
  •         VCard vcard = new VCard();
  •         vcard.load(connection);
  •             byte[] bytes;
  •                 bytes = getFileBytes(f);
  •                 String encodedImage = StringUtils.encodeBase64(bytes);
  •                 vcard.setAvatar(bytes, encodedImage);
  •                 vcard.setEncodedImage(encodedImage);
  •                 vcard.setField("PHOTO", "<TYPE>image/jpg</TYPE><BINVAL>"
  •                         + encodedImage + "</BINVAL>", true);
  •                 ByteArrayInputStream bais = new ByteArrayInputStream(
  •                         vcard.getAvatar());
  •                 Image image = ImageIO.read(bais);
  •                 ImageIcon ic = new ImageIcon(image);
  •             vcard.save(connection);
  •     }
  •     private static byte[] getFileBytes(File file) throws IOException {
  •         BufferedInputStream bis = null;
  •         try {
  •             bis = new BufferedInputStream(new FileInputStream(file));
  •             int bytes = (int) file.length();
  •             byte[] buffer = new byte[bytes];
  •             int readBytes = bis.read(buffer);
  •             if (readBytes != buffer.length) {
  •                 throw new IOException("Entire file not read");
  •             }
  •             return buffer;
  •         } finally {
  •             if (bis != null) {
  •                 bis.close();
  •             }
  •         }
  • }

4.  补充,用户状态的监听

即对方改变头像,状态,心情时,更新自己用户列表,其实这里已经有smack实现的监听器




[java] view plaincopyprint?

  • final Roster roster = Client.getRoster();
  •         roster.addRosterListener(
  •                 new RosterListener() {
  •                     @Override
  •                     public void entriesAdded(Collection<String> arg0) {
  •                         // TODO Auto-generated method stub
  •                         System.out.println("--------EE:"+"entriesAdded");
  •                     }
  •                     @Override
  •                     public void entriesDeleted(Collection<String> arg0) {
  •                         // TODO Auto-generated method stub
  •                         System.out.println("--------EE:"+"entriesDeleted");
  •                     }
  •                     @Override
  •                     public void entriesUpdated(Collection<String> arg0) {
  •                         // TODO Auto-generated method stub
  •                         System.out.println("--------EE:"+"entriesUpdated");
  •                     }
  •                     @Override
  •                     public void presenceChanged(Presence arg0) {
  •                         // TODO Auto-generated method stub
  •                         System.out.println("--------EE:"+"presenceChanged");
  •                     }
  •                 });



下一篇主要是文件传输和接收
[5]文件传输及离线消息的获取三天时间,赶在最后一下午实现了文件的传输,本来需要实现离线文件的发送的,一直没想好怎么弄,找openfire的离线文件插件没找到,后来想出一种方法,起服务器时起了一个系统用户,一直在线,当用户发送离线文件,检测到对方不存在,先发给系统用户,存到服务器路径,并在数据库中保存信息,当对方上线时,系统用户查表,拿文件发送

想是这么想的,问题是时间太紧,没有实现,囧。
下一篇写离线消息和离线文件
1.       文件的发送
开一个文件选择框,选中文件后再调用下面的方法

[java] view plaincopyprint?

  • public static void sendFile(XMPPConnection connection,
  •             String user, File file) throws XMPPException, InterruptedException {
  •         System.out.println("发送文件开始"+file.getName());
  •         FileTransferManager transfer = new FileTransferManager(Client.getConnection());
  •         System.out.println("发送文件给: "+user+Client.getServiceNameWithPre());
  •         OutgoingFileTransfer out = transfer.createOutgoingFileTransfer(user+Client.getServiceNameWithPre()+"/Smack");//
  •         out.sendFile(file, file.getName());
  •         System.out.println("//////////");
  •         System.out.println(out.getStatus());
  •         System.out.println(out.getProgress());
  •         System.out.println(out.isDone());
  •         System.out.println("//////////");
  •         System.out.println("发送文件结束");
  •     }



2.       文件接收,必须使用监听

[java] view plaincopyprint?

  • FileTransferManager transfer = new FileTransferManager(connection);
  • transfer.addFileTransferListener(new RecFileTransferListener());
  • public class RecFileTransferListener implements FileTransferListener {
  •     public String getFileType(String fileFullName)
  •     {
  •         if(fileFullName.contains("."))
  •         {
  •             return "."+fileFullName.split("//.")[1];
  •         }else{
  •             return fileFullName;
  •         }
  •     }
  •     @Override
  •     public void fileTransferRequest(FileTransferRequest request) {
  •         System.out.println("接收文件开始.....");
  •         final IncomingFileTransfer inTransfer = request.accept();
  •         final String fileName = request.getFileName();
  •         long length = request.getFileSize();
  •         final String fromUser = request.getRequestor().split("/")[0];
  •         System.out.println("文件大小:"+length + "  "+request.getRequestor());
  •         System.out.println(""+request.getMimeType());
  •         try {
  •             JFileChooser chooser = new JFileChooser();
  •             chooser.setCurrentDirectory(new File("."));
  •             int result = chooser.showOpenDialog(null);
  •             if(result==JFileChooser.APPROVE_OPTION)
  •             {
  •                 final File file = chooser.getSelectedFile();
  •                 System.out.println(file.getAbsolutePath());
  •                     new Thread(){
  •                         public void run()
  •                         {
  •                         try {
  •                             System.out.println("接受文件: " + fileName);
  •                             inTransfer
  •                                     .recieveFile(new File(file
  •                                             .getAbsolutePath()
  •                                             + getFileType(fileName)));
  •                             Message message = new Message();
  •                             message.setFrom(fromUser);
  •                             message.setProperty("REC_SIGN", "SUCCESS");
  •                             message.setBody("["+fromUser+"]发送文件: "+fileName+"/r/n"+"存储位置: "+file.getAbsolutePath()+ getFileType(fileName));
  •                             if (Client.isChatExist(fromUser)) {
  •                                 Client.getChatRoom(fromUser).messageReceiveHandler(
  •                                         message);
  •                             } else {
  •                                 ChatFrameThread cft = new ChatFrameThread(
  •                                         fromUser, message);
  •                                 cft.start();
  •                             }
  •                         } catch (Exception e2) {
  •                             e2.printStackTrace();
  •                         }
  •                         }
  •                     }.start();
  •             }else{
  •                 System.out.println("拒绝接受文件: "+fileName);
  •                 request.reject();
  •                 Message message = new Message();
  •                 message.setFrom(fromUser);
  •                 message.setBody("拒绝"+fromUser+"发送文件: "+fileName);
  •                 message.setProperty("REC_SIGN", "REJECT");
  •                 if (Client.isChatExist(fromUser)) {
  •                     Client.getChatRoom(fromUser)
  •                             .messageReceiveHandler(message);
  •                 } else {
  •                     ChatFrameThread cft = new ChatFrameThread(
  •                             fromUser, message);
  •                     cft.start();
  •                 }
  •             }
  •             /* InputStream in = inTransfer.recieveFile();
  •              String fileName = "r"+inTransfer.getFileName();
  •              OutputStream out = new FileOutputStream(new File("d:/receive/"+fileName));
  •              byte[] b = new byte[512];
  •              while(in.read(b) != -1)
  •              {
  •                  out.write(b);
  •                  out.flush();
  •              }
  •              in.close();
  •              out.close();*/
  •         } catch (Exception e) {
  •             e.printStackTrace();
  •         }
  •         System.out.println("接收文件结束.....");
  •     }
  • }





晕死,在演示的时候竟然发送文件崩盘了。。。。。实在无语
对了,在发送文件的createOutgoing那边有问题,貌似/Smack,哎,对spark发送就不成功

短短三天,查资料差得头晕,中文的信息貌似少之又少,哎,匆匆完成,只能算是个半成品,大家自己完善吧。

呵呵,下一篇最后一篇了,谢谢离线消息和离线文件吧
[6]离线消息和离线文件的实现终篇,三天所学所用,也就这些,如果需要大家要自己去查资料研究研究,功能其实可以很强大的
可惜界面做得不好,一大短处,从大一迄今没整好,主要是个人审美不行,哎

毕业季呀毕业季,明天摆摊卖书,再半月就可能和生活四年的兄弟姐妹说再见,考研考公务员工作的,各奔东西了,我也将南下杭州
感慨,天下无不散之筵席
在此祝所有刚毕业的,学业事业有成吧

不废话了,貌似最近太感性了,理科男,伤不起呀

1.离线消息
  openfire本身是支持离线消息的,不需要进行额外处理,可以用spark测试下
  使用smack,其实他提供了相应的方法
  Class OfflineMessageManager

  可以看下描述

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.

英文退化了点,汗,大意就是,必须在发送在线信息之前去获取离线消息  

刚开始没看这个,结果在上线之后,去取,结果。。。。离线消息数量总是为零,囧

首先,连接,状态要设为离线

[java] view plaincopy

  • ConnectionConfiguration connConfig = new ConnectionConfiguration(serverDomain);  
  •    
  •   connConfig.setSendPresence(false); // where connConfig is object of .  
  •   
  •      connection = new XMPPConnection(connConfig);  
  •      connection.connect();  


然后,登陆
  connection.login(userName, pwd);

接着,拿离线消息

[java] view plaincopy

  • OfflineMessageManager offlineManager = new OfflineMessageManager(  
  •                 Client.getConnection());  
  •         try {  
  •             Iterator<org.jivesoftware.smack.packet.Message> it = offlineManager  
  •                     .getMessages();  
  •   
  •             System.out.println(offlineManager.supportsFlexibleRetrieval());  
  •             System.out.println("离线消息数量: " + offlineManager.getMessageCount());  
  •   
  •               
  •             Map<String,ArrayList<Message>> offlineMsgs = new HashMap<String,ArrayList<Message>>();  
  •               
  •             while (it.hasNext()) {  
  •                 org.jivesoftware.smack.packet.Message message = it.next();  
  •                 System.out  
  •                         .println("收到离线消息, Received from 【" + message.getFrom()  
  •                                 + "】 message: " + message.getBody());  
  •                 String fromUser = message.getFrom().split("/")[0];  
  •   
  •                 if(offlineMsgs.containsKey(fromUser))  
  •                 {  
  •                     offlineMsgs.get(fromUser).add(message);  
  •                 }else{  
  •                     ArrayList<Message> temp = new ArrayList<Message>();  
  •                     temp.add(message);  
  •                     offlineMsgs.put(fromUser, temp);  
  •                 }  
  •             }  
  •   
  •             //在这里进行处理离线消息集合......  
  •             Set<String> keys = offlineMsgs.keySet();  
  •             Iterator<String> offIt = keys.iterator();  
  •             while(offIt.hasNext())  
  •             {  
  •                 String key = offIt.next();  
  •                 ArrayList<Message> ms = offlineMsgs.get(key);  
  •                 TelFrame tel = new TelFrame(key);  
  •                 ChatFrameThread cft = new ChatFrameThread(key, null);  
  •                 cft.setTel(tel);  
  •                 cft.start();  
  •                 for (int i = 0; i < ms.size(); i++) {  
  •                     tel.messageReceiveHandler(ms.get(i));  
  •                 }  
  •             }  
  •               
  •               
  •             offlineManager.deleteMessages();  
  •         } catch (Exception e) {  
  •             e.printStackTrace();  
  •         }  


记得最后要把离线消息删除,即通知服务器删除离线消息
offlineManager.deleteMessages();
否则,下次上了消息还存在
接着,上线
Presence presence = new Presence(Presence.Type.available);
        connection.sendPacket(presence);

2.离线文件

这个我没实现,汗
主要思想:开发openfire插件,拦截离线文件,将文件存到服务器上,同时在数据库里开一张表,存储文件信息
               当用户上线时,查表,若是有,根据路径,拿了发送
当然,大家可以谷歌下是否有相应的插件,时间紧迫,我倒是没找着



到这里,大概就这些了,对了,还扩展了个视频音频聊天,不过使用的是JMF,点对点的,本来打算使用jingle的,结果连API文档都没找到,晕死

就这些


楼主
发表于 2013-4-19 09:41:37
回复

使用道具 举报

xmpp smack类库实现聊天功能 [复制链接]

LZ,你为什么不开发一款聊天软件呢?
沙发
发表于 2013-4-20 17:36:46
回复

使用道具 举报

xmpp smack类库实现聊天功能 [复制链接]

{:3_60:}这篇帖子好长呀。。
板凳
发表于 2013-4-21 00:17:45
回复

使用道具 举报

xmpp smack类库实现聊天功能 [复制链接]

本帖最后由 lixiao 于 2013-4-22 16:59 编辑

开头就没看懂
Smack是一个开源,易于使用的XMPP(jabber)客户端类库。
   Smack API, 是一个 Java 的XMPP Client Library,也是由Jive Software开发。 优点:编程简单。 缺点:API并非为大量并发用户设计,每个客户要1个线程,占用资源大,1台机器只能模拟有限(数千个)客户.
smack是一个用 java 写的XMPP客户端代码库, 是 spark 的核心.

说是客户端类库,但服务器才需要考虑大量并发用户的问题啊

另外你能不能详细介绍一下xmpp通信的内部stack到底是什么样的,JMF和jingle又是什么,为什么它们能合作?光是个聊天软件的话,我看着API也能写出来啊。
地板
发表于 2013-4-22 16:55:31
回复

使用道具 举报

xmpp smack类库实现聊天功能 [复制链接]

NingerJohn 发表于 2013-4-20 17:36
LZ,你为什么不开发一款聊天软件呢?

爆料:他正在开发
5#
发表于 2013-4-23 09:15:57
回复

使用道具 举报

xmpp smack类库实现聊天功能 [复制链接]

jonaszhang 发表于 2013-4-23 09:15
爆料:他正在开发

真的吗? {:7_144:}
6#
发表于 2013-4-23 12:49:59
回复

使用道具 举报

xmpp smack类库实现聊天功能 [复制链接]

楼主您好,如果我现在需要将本地相册中的图片(类似于文件传输)发送给另一个客户端,需要怎么获取图片名称进行传送呢
7#
发表于 2013-9-3 15:56:22
回复

使用道具 举报

xmpp smack类库实现聊天功能 [复制链接]

一点也看不懂哦,好难啊
8#
发表于 2013-9-3 16:52:46
回复

使用道具 举报

openKylin

GMT+8, 2024-6-1 14:33 , Processed in 0.022433 second(s), 17 queries , Gzip On.

Copyright ©2022 openKylin. All Rights Reserved .

ICP No. 15002470-12 Tianjin

快速回复 返回顶部 返回列表