利用openfire和smark的即时通信

2014-06-26 09:12 3240人阅读 评论(5) 收藏 举报
 分类:
Android(3) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

服务器: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包里面含有发送离线消息、文件等类

下面是创建一个连接

[java] view plaincopy
  1. ConnectionConfiguration config = new ConnectionConfiguration("ip", 5222);
  2. //设置成disabled,则不会去验证服务器证书是否有效,默认为enabled
  3. config.setSecurityMode(SecurityMode.disabled);
  4. //设置可以调试,默认为false,老版本的写法为XMPPConnection.DEBUG_ENABLED = true;
  5. config.setDebuggerEnabled(true);
  6. //设置是否在登陆的时候告诉服务器,默认为true
  7. config.setSendPresence(false);
  8. //XMPPConnection在后来的版本中改成了抽象类
  9. XMPPConnection conn = new XMPPTCPConnection(config);
  10. //设置等待时间
  11. conn.setPacketReplyTimeout(5000);
  12. conn.connect();
  13. //用户名,密码,资源名(例如:如果是用潘迪安发送的消息,则资源名就是:  潘迪安,用于标识客户端)
  14. conn.login("admin", "0", "资源名");

关于连接的参数,在新版本中全部在config中设置

发送消息

[html] view plaincopy
  1. private void testSendMessage(XMPPConnection conn) throws Exception {
  2. //jid在数据表中ofroster可以查到,一般是   用户名@服务器名称
  3. Chat chat = ChatManager.getInstanceFor(conn).createChat("ly@192.168.1.100", new MessageListener() {
  4. @Override
  5. public void processMessage(Chat chat, Message message) {
  6. System.out.println("Received message: " + message);
  7. }
  8. });
  9. Message msg = new Message();
  10. msg.setBody("hello world");
  11. //定义成normal,在对象不在线时发送离线消息,消息存放在数据表ofoffline中
  12. msg.setType(Message.Type.normal);
  13. //发送消息,参数可以是字符串,也可以是message对象
  14. chat.sendMessage(msg);
  15. //发送广播
  16. conn.sendPacket(msg);
  17. }

发送离线消息

[java] view plaincopy
  1. private void testOffLine(XMPPConnection conn) throws Exception {
  2. //离线文件
  3. OfflineMessageManager offMM = new OfflineMessageManager(conn);
  4. System.out.println("离线文件数量 :" + offMM.getMessageCount());
  5. System.out.println("离线文件内容 :");
  6. //经测试,当调用getMessages时,会触发chat设置的监听器,从而输出离线消息内容,但是getMessages方法返回的离线消息为空
  7. //猜测回调函数的触发条件是一个变量,方变量改变时(while(flag)),执行回调函数
  8. List<Message> listMessage = offMM.getMessages();
  9. //listMessage的大小为0
  10. System.out.println(listMessage.size());
  11. for(Message m : offMM.getMessages()) {
  12. System.out.println(" 离线  : " + m.getBody() + m.getBodies());
  13. }
  14. }

得到好友列表

[java] view plaincopy
  1. private void testGetRoster(XMPPConnection conn) throws Exception {
  2. //得到该user的roster(相当于好友列表),不区分是否在线
  3. Roster r = conn.getRoster();
  4. Collection<RosterEntry> c = r.getEntries();
  5. for(RosterEntry re : c) {
  6. StringBuilder sb = new StringBuilder();
  7. sb.append("name : ").append(re.getName());
  8. sb.append("\nuser : ").append(re.getUser());
  9. sb.append("\ntype : ").append(re.getType());
  10. sb.append("\nstatus : ").append(re.getStatus());
  11. System.out.println(sb.toString());
  12. System.out.println("-----------------------------");
  13. }
  14. System.out.println(r.getEntries());
  15. //输出内容
  16. /*  name : null
  17. user : ly@192.168.1.100
  18. type : from
  19. status : null
  20. -----------------------------
  21. name : null
  22. user : yy@192.168.1.100
  23. type : to
  24. status : null
  25. -----------------------------
  26. [ly@192.168.1.100, yy@192.168.1.100]
  27. */
  28. }

管理好友,监听好友请求

[java] view plaincopy
  1. <pre name="code" class="java">
[java] view plaincopy
  1. </pre><pre name="code" class="java">private void testAddAndDelFriends(final XMPPConnection conn) throws Exception {
  2. Roster r = conn.getRoster();
  3. // 用户的jid,昵称,用户的分组。如果该用户不存在也可以添加
  4. //  r.createEntry("yy@192.168.1.100", "yy", null);
  5. // rosterEntry的构造方法是包访问权限,不能直接new
  6. //  RosterEntry entry = r.getEntry("ly@192.168.1.100");
  7. // r.removeEntry(entry);
  8. //监听所有的请求,之后可以过滤掉不想要的请求
  9. PacketListener packetListener = new PacketListener() {
  10. @Override
  11. public void processPacket(Packet packet) throws NotConnectedException {
  12. /*
  13. available
  14. unavailable
  15. subscribe  发出添加好友的请求
  16. subscribed 同意添加好友
  17. unsubscribe 发出删除好友请求
  18. unsubscribed 删除好友(即拒绝添加好友),
  19. 备注:对方发出添加好友的请求后,在服务器端会自动把对方加入到自己的roster,所以在执行处理好友请求或添加删除好友的时候,要重新获取roster,更新好友列表
  20. */
  21. Presence presence = (Presence) packet;
  22. Type type = presence.getType();
  23. //请求添加好友
  24. if(Type.subscribe.equals(type)) {
  25. //注意点:要设置to(即指明要发送的对象,否则不能成功拒绝),至于from不用设置,因为在sendPacket方法中已经设置了,formMode初始化的时候为OMITTED,可以自己设置
  26. /*
  27. switch (fromMode) {
  28. case OMITTED:
  29. packet.setFrom(null);
  30. break;
  31. case USER:
  32. packet.setFrom(getUser());//getUser是抽象方法
  33. break;
  34. */
  35. //直接用传来的presence,不能自己新建一个presence(可能要验证presence是否是原来的对象,来判断是谁拒绝了谁的好友请求),否则不能成功拒绝对方添加好友
  36. //例:A--presence1-->B   A---presence2---C, C---presence3---A这样服务器就没办法判断是B、C中的哪一个拒绝了A的请求
  37. presence.setType(Type.unsubscribed);//拒绝,发送了一条presence
  38. //presence.setType(Type.unavailable);//发送了两条presence,一条是subscribed,一条是unavailabled,能接受对方消息,自己的状态显示隐身,再一次登录的时候显示在线
  39. presence.setTo(presence.getFrom());
  40. presence.setPacketID(presence.getPacketID());
  41. Roster r = conn.getRoster();
  42. try {
  43. RosterEntry entry = r.getEntry(presence.getFrom());
  44. if(entry != null)
  45. r.removeEntry(entry);
  46. } catch (NotLoggedInException | NoResponseException | XMPPErrorException e) {
  47. // TODO Auto-generated catch block
  48. e.printStackTrace();
  49. }
  50. conn.sendPacket(presence);
  51. //多方删除自己
  52. } else if(Type.unsubscribe.equals(type)) {
  53. presence.setTo(presence.getFrom());
  54. presence.setType(Type.unsubscribe);
  55. Roster r = conn.getRoster();
  56. try {
  57. r.removeEntry(r.getEntry(presence.getFrom()));
  58. } catch (NotLoggedInException | NoResponseException | XMPPErrorException e) {
  59. // TODO Auto-generated catch block
  60. e.printStackTrace();
  61. }
  62. conn.sendPacket(presence);
  63. }
  64. }
  65. };
  66. //      PacketFilter packetFilter = new PacketFilter() {
  67. //
  68. //          //如果返回false,则不把事件交给listener处理,否则会调用packetListener中的processPacket方法
  69. //          //方法解释true if and only if packet passes the filter.
  70. //          @Override
  71. //          public boolean accept(Packet packet) {
  72. //              System.out.println("2" + packet);
  73. //              return true;
  74. //          }
  75. //      };
  76. //过滤掉所有的不是好友请求、删除的所有packet
  77. PacketFilter packetFilter = new AndFilter(new PacketTypeFilter(Presence.class));
  78. conn.addPacketListener(packetListener, packetFilter);
  79. //未知
  80. RosterExchangeManager rem = new RosterExchangeManager(conn);
  81. rem.addRosterListener(new RosterExchangeListener() {
  82. @Override
  83. public void entriesReceived(String from, Iterator<RemoteRosterEntry> remoteRosterEntries) {
  84. System.out.println(from);
  85. while(remoteRosterEntries.hasNext()) {
  86. RemoteRosterEntry entry = remoteRosterEntries.next();
  87. System.out.println(entry.getUser() + " : " + entry.getName());
  88. }
  89. }
  90. });
  91. }

得到好友的信息,主要是VCard类的使用

[java] view plaincopy
  1. private void testGetFriendInfo(XMPPConnection conn) throws Exception {
  2. VCard vCard = new VCard();
  3. VCardManager vcManager = new VCardManager();
  4. //此处返回false
  5. boolean b = vcManager.isSupported("ly@192.168.1.100", conn);
  6. System.out.println(b);
  7. vCard.load(conn, "ly@192.168.1.100");
  8. // Load Avatar from VCard
  9. byte[] avatarBytes = vCard.getAvatar();
  10. //得不到头像等的信息
  11. if(avatarBytes == null) {
  12. return;
  13. }
  14. // To create an ImageIcon for Swing applications
  15. ImageIcon icon = new ImageIcon(avatarBytes);
  16. System.out.println(icon.getIconWidth() + " : " + icon.getIconHeight());
  17. // To create just an image object from the bytes
  18. ByteArrayInputStream bais = new ByteArrayInputStream(avatarBytes);
  19. try {
  20. Image image = ImageIO.read(bais);
  21. FileOutputStream fos = new FileOutputStream("D://icon.jpg");
  22. fos.write(avatarBytes);
  23. fos.close();
  24. }
  25. catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. }

设置自己的状态信息

[java] view plaincopy
  1. private void testSetInfo(XMPPConnection conn) throws Exception {
  2. VCard vCard = new VCard();
  3. vCard.load(conn);
  4. vCard.setEmailHome("admin@126.com");
  5. vCard.setAddressFieldWork("POSTAL", "汇宝大厦");
  6. //修改完要保存修改的内容,否则没办法更新到服务器
  7. vCard.save(conn);
  8. //修改自身的状态,包括隐身,上线(可以指定对特定的好友更改状态)
  9. Presence p = new Presence(Type.available);
  10. p.setTo("ly@192.168.1.100");
  11. //修改心情
  12. p.setStatus("我的心情");
  13. //同样要发到服务器
  14. conn.sendPacket(p);
  15. }

监听好友的状态

[java] view plaincopy
  1. private void testSetRosterListener(XMPPConnection conn) throws Exception {
  2. Roster r = conn.getRoster();
  3. r.createEntry("ly@192.168.1.100", "昵称", null);
  4. r.addRosterListener(new RosterListener() {
  5. @Override
  6. public void presenceChanged(Presence presence) {
  7. //更改状态信息时调用该方法(更改在线状态,修改心情,修改头像等)
  8. System.out.println("presenceChanged");
  9. }
  10. @Override
  11. public void entriesUpdated(Collection<String> addresses) {
  12. //该方法以及下面的方法都是在服务器修改好友信息时触发
  13. System.out.println("entriesUpdated");
  14. }
  15. @Override
  16. public void entriesDeleted(Collection<String> addresses) {
  17. // TODO Auto-generated method stub
  18. System.out.println("entriesDeleted");
  19. }
  20. @Override
  21. public void entriesAdded(Collection<String> addresses) {
  22. // TODO Auto-generated method stub
  23. System.out.println("entriesAdded");
  24. }
  25. });
  26. }

监听好友的输入状态

[java] view plaincopy
  1. private void testGetExtention(XMPPConnection conn) throws Exception {
  2. Chat chat = ChatManager.getInstanceFor(conn).createChat("ly@192.168.1.100", new MessageListener() {
  3. @Override
  4. public void processMessage(Chat chat, Message message) {
  5. //得到输入状态,分为五种:正在输入(composing),暂停输入(paused),发送(active),关闭对话框(gone)
  6. PacketExtension pe = message.getExtension("http://jabber.org/protocol/chatstates");
  7. switch (pe.getElementName()) {
  8. case "composing":
  9. System.out.println("正在输入......");
  10. break;
  11. case "paused":
  12. System.out.println("正在冥想......");
  13. break;
  14. case "active":
  15. System.out.println("对方已发送。");
  16. break;
  17. case "gone":
  18. System.out.println("对话框已被关闭。");
  19. break;
  20. default:
  21. break;
  22. }
  23. }
  24. });
  25. Message msg = new Message();
  26. msg.addExtension(new ChatStateExtension(ChatState.gone));
  27. msg.setBody("hello world");
  28. chat.sendMessage(msg);
  29. }

加入聊天室进行多人聊天

[java] view plaincopy
  1. private MultiUserChat multiUserChat;
  2. private void testMutiUserChat(XMPPConnection conn) throws Exception {
  3. MultiUserChat.addInvitationListener(conn, new InvitationListener() {
  4. @Override
  5. public void invitationReceived(XMPPConnection conn, String room, String inviter, String reason, String password, Message message) {
  6. StringBuilder sb = new StringBuilder();
  7. sb.append("房间号  : ").append(room);
  8. sb.append("\n邀请者  : ").append(inviter);
  9. sb.append("\n理由  : ").append(reason);
  10. sb.append("\n密码  : ").append(password);
  11. sb.append("\n消息  : ").append(message);
  12. System.out.println(sb);
  13. multiUserChat = new MultiUserChat(conn, room);
  14. try {
  15. multiUserChat.join("admin", password);
  16. } catch (XMPPErrorException e) {
  17. // TODO Auto-generated catch block
  18. e.printStackTrace();
  19. } catch (SmackException e) {
  20. // TODO Auto-generated catch block
  21. e.printStackTrace();
  22. }
  23. multiUserChat.addMessageListener(new PacketListener() {
  24. @Override
  25. public void processPacket(Packet packet) throws NotConnectedException {
  26. Message msg = (Message) packet;
  27. System.out.println(msg.getBody());
  28. }
  29. });
  30. }
  31. });
  32. while(true) {
  33. try {
  34. Thread.sleep(500);
  35. if(multiUserChat == null)
  36. continue;
  37. //关于发送消息的问题,可以直接发字符串
  38. //也可以发送message,但是要设定message的一些参数,否则不能发送(参数设置如下)
  39. //用Chat发送消息时,不用设置,原因是在Chat的sendMessage方法中已经添加了这些参数
  40. /*
  41. *  message.setTo(participant);
  42. message.setType(Message.Type.chat);
  43. message.setThread(threadID);
  44. */
  45. //但是,用MultiUserChat类中的sendMessage方法,直接调用了XMPPConnection中的sendPacket方法,没有设置Message的参数
  46. Message msg = new Message();
  47. //房间名称
  48. msg.setTo("a@conference.192.168.1.100");
  49. msg.setType(Message.Type.groupchat);
  50. msg.setThread(Thread.currentThread().getId() + "");
  51. msg.setBody("hello");
  52. multiUserChat.sendMessage(msg);
  53. break;
  54. } catch (InterruptedException e) {
  55. e.printStackTrace();
  56. } catch (NotConnectedException e) {
  57. e.printStackTrace();
  58. } catch (XMPPException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. }

发送和接收文件

[java] view plaincopy
  1. private void testSendFile(XMPPConnection conn) throws Exception {
  2. // 发送文件的管理器
  3. FileTransferManager ftm = new FileTransferManager(conn);
  4. ftm.addFileTransferListener(new FileTransferListener() {
  5. @Override
  6. public void fileTransferRequest(FileTransferRequest request) {
  7. System.out.println(request.getFileName());
  8. IncomingFileTransfer inComingFileTransfer = request.accept();
  9. try {
  10. //可以直接写到file文件中
  11. File file = new File("D://" + request.getFileName());
  12. inComingFileTransfer.recieveFile(file);
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. });
  18. // 注意jid格式,下面为标准格式,如果不对则会抛出jid格式错误的异常
  19. // (if (parseName(jid).length() <= 0 || parseServer(jid).length() <= 0|| parseResource(jid).length() <= 0) {
  20. // return false;
  21. OutgoingFileTransfer oft = ftm.createOutgoingFileTransfer("admin@192.168.1.100/潘迪安");
  22. File file = new File("D://time.jpg");
  23. oft.sendFile(file, "图片");
  24. System.out.println(oft.isDone());
  25. }

创建多人聊天室

[java] view plaincopy
  1. private void testCreateRoom(XMPPConnection conn) throws Exception {
  2. while(true) {
  3. if(conn != null)
  4. break;
  5. }
  6. //@之前的是会议房间名称,之后的是conference+ip(固定格式,不能改变)
  7. MultiUserChat muc = new MultiUserChat(conn, "ly@conference.192.168.1.100");
  8. //昵称,如果该房间已经存在,则会抛出Creation failed - Missing acknowledge of room creation.(先加入房间,然后离开房间)
  9. muc.create("real_admin");
  10. Form form = muc.getConfigurationForm();
  11. Form submitForm = form.createAnswerForm();
  12. //下面的初始化有什么用,在创建submitForm的时候已经设置参数了
  13. //      List<FormField> list = submitForm.getFields();
  14. //      for(FormField f : list) {
  15. //          if(!(FormField.TYPE_HIDDEN.equals(f.getType())) && f.getVariable() != null) {
  16. //              submitForm.setDefaultAnswer(f.getVariable());
  17. //          }
  18. //      }
  19. //参数到底是什么意思,为什么有的可以设置,有的不可以设置
  20. /*
  21. *  variable:FORM_TYPE  type:hidden  value:[http://jabber.org/protocol/muc#roomconfig]
  22. variable:muc#roomconfig_roomname  type:text-single  value:[]
  23. variable:muc#roomconfig_roomdesc  type:text-single  value:[]
  24. variable:muc#roomconfig_changesubject  type:boolean  value:[]
  25. variable:muc#roomconfig_maxusers  type:list-single  value:[]
  26. variable:muc#roomconfig_presencebroadcast  type:list-multi  value:[]
  27. variable:muc#roomconfig_publicroom  type:boolean  value:[]
  28. variable:muc#roomconfig_persistentroom  type:boolean  value:[]
  29. variable:muc#roomconfig_moderatedroom  type:boolean  value:[]
  30. variable:muc#roomconfig_membersonly  type:boolean  value:[]
  31. variable:muc#roomconfig_allowinvites  type:boolean  value:[]
  32. variable:muc#roomconfig_passwordprotectedroom  type:boolean  value:[]
  33. variable:muc#roomconfig_roomsecret  type:text-private  value:[]
  34. variable:muc#roomconfig_whois  type:list-single  value:[]
  35. variable:muc#roomconfig_enablelogging  type:boolean  value:[]
  36. variable:x-muc#roomconfig_reservednick  type:boolean  value:[]
  37. variable:x-muc#roomconfig_canchangenick  type:boolean  value:[]
  38. variable:x-muc#roomconfig_registration  type:boolean  value:[]
  39. variable:muc#roomconfig_roomadmins  type:jid-multi  value:[]
  40. variable:muc#roomconfig_roomowners  type:jid-multi  value:[]
  41. */
  42. //submitForm.setAnswer(FormField.TYPE_TEXT_PRIVATE, "0");
  43. muc.sendConfigurationForm(submitForm);
  44. //被拒绝时执行
  45. muc.addInvitationRejectionListener(new InvitationRejectionListener() {
  46. @Override
  47. public void invitationDeclined(String invitee, String reason) {
  48. System.out.println(invitee + " : " + reason);
  49. }
  50. });
  51. muc.invite("yy@192.168.1.100", "ly_room");
  52. }

管理房间

[java] view plaincopy
  1. <pre name="code" class="java">private void testManageRoom(XMPPConnection conn) throws Exception {
  2. testCreateRoom(conn);
  3. MultiUserChat muc = new MultiUserChat(conn, "ly@conference.192.168.1.100");
  4. //Thread.sleep(5000);
  5. //赋予管理员权限
  6. //muc.grantAdmin("yy@192.168.1.100");
  7. //Thread.sleep(5000);
  8. //如果是管理员,则不能踢除
  9. //muc.banUser("yy@192.168.1.100", "太水");
  10. //收回说话的权限
  11. muc.revokeVoice("yy");
  12. //muc.grantVoice("yy");
  13. }

注册

[java] view plaincopy
  1. private void testRegister(XMPPConnection conn) throws Exception {
  2. //可以直接改登陆用户的信息(如果是username的值必须和该用户的用户名相同)
  3. Registration r = new Registration();
  4. Map<String, String> attributes = new HashMap<String, String>();
  5. attributes.put("username", "newuser");
  6. attributes.put("password", "0");
  7. attributes.put("email", "new00@126.com");
  8. attributes.put("name", "name@192.168.1.100");
  9. //添加用户,要设置type类型为set,原因不明
  10. r.setType(IQ.Type.SET);
  11. r.setAttributes(attributes);
  12. //过滤器,用来过滤由服务器返回的信息(即得到注册信息的内容)
  13. PacketFilter packetFilter = new AndFilter(new PacketIDFilter(r.getPacketID()), new PacketTypeFilter(IQ.class));
  14. PacketCollector collector = conn.createPacketCollector(packetFilter);
  15. System.out.println(r);
  16. conn.sendPacket(r);
  17. IQ result = (IQ) collector.nextResult();
  18. if(result == null) {
  19. System.out.println("服务器没有返回任何信息");
  20. } else {
  21. switch (result.getType().toString()) {
  22. case "result":
  23. System.out.println("注册成功");
  24. break;
  25. case "error":
  26. if(result.getError().toString().equalsIgnoreCase("conflict"))
  27. System.out.println("用户名称已存在");
  28. else
  29. System.out.println("注册失败");
  30. break;
  31. default:
  32. break;
  33. }
  34. }
  35. }

管理账号密码

[java] view plaincopy
  1. private void testModifyPwd(XMPPConnection conn) throws Exception {
  2. //创建一个用户信息管理,可以创建新用户,或者修改用户密码
  3. AccountManager am = AccountManager.getInstance(conn);
  4. Collection<String> c = am.getAccountAttributes();
  5. for(String s : c) {
  6. System.out.println(s);
  7. }
  8. /*
  9. * 通过accountManager可以得到的属性
  10. *  username
  11. email
  12. registered
  13. name
  14. password
  15. */
  16. am.getAccountAttribute("username");
  17. am.createAccount("newUser", "0");
  18. am.changePassword("00");
  19. }

至于细节和中间遇到的问题,在程序代码中都有叙述

参考博客:

http://blog.csdn.NET/shimiso/article/details/11225873

利用openfire和smark的即时通信相关推荐

  1. 基于XMPP的即时通信

    一.Openfire服务器和Spark客户端的安装 1.在下列下载地址下载Openfire和Spark http://www.igniterealtime.org/downloads/index.js ...

  2. 开源企业即时通信解决方案

    开源企业即时通信解决方案 (2007-04-25 15:33:37) 转载▼     本文使用 Openfire 来解决企业即时通信平台,服务端选用Linux操作系统.客户端为windows平台. 第 ...

  3. 【即时通信】openfire安装和配置讲解

    文章目录 概述 一.下载openfire,解压就可以openfire启动服务.这里我安装openfire在linux服务器上.服务器地址:10.119.9.149. 二.准备openfire所需的数据 ...

  4. java即时通信解决方案openfire+spark完整安装指南

    本文介绍基于java的即时通信解决方案openfire3.6.4+spark2.5.8的完整的安装说明,并介绍了使用smack的java类库如何与im用户交互的例子,这样使得业务系统的消息可以通过sm ...

  5. 基于XMPP协议的Android即时通信系

    2019独角兽企业重金招聘Python工程师标准>>> 以前做过一个基于XMPP协议的聊天社交软件,总结了一下.发出来. 设计基于开源的XMPP即时通信协议,采用C/S体系结构,通过 ...

  6. xmpp协议(即时通信协议规范)

    转载自 https://www.cnblogs.com/jiyuqi/p/5085932.html 相关背景 IM(Instant Messaging)正在被广泛使用,特别是公司与它们的客户互动连接方 ...

  7. iOS开发之使用XMPPFramework实现即时通信(二)

    上篇的博客iOS开发之使用XMPPFramework实现即时通信(一)只是本篇的引子,本篇博客就给之前的微信加上即时通讯的功能,主要是对XMPPFramework的使用.本篇博客中用到了Spark做测 ...

  8. WebSocket 实现 Web 端即时通信

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:牛人 20000 字的 Spring Cloud 总结,太硬核了~ 前言 WebSocket 是HTML5开始提 ...

  9. 中油即时通信电脑版_市场营销之即时通讯营销

    ✎ IM营销又叫即时通讯营销(instantmessaging),是企业通过即时工具im推广产品和品牌,以实现目标客户挖掘和转化的网络营销方式. 封面设计丨Sweety 责编丨花花 第60篇丨每日一篇 ...

最新文章

  1. java-集合排序,队列,散列表map以及如何遍历
  2. Spring ORM数据訪问——Hibernate
  3. SQL注入思维导图【新手全面非详细】
  4. jvm默认垃圾收集器
  5. 吴恩达神经网络1-2-2_图神经网络进行药物发现-第2部分
  6. sdk和api有什么区别
  7. Leetcode-1154 Ordinal Number Of Date(一年中的第几天)
  8. 字符验证码识别项目记录
  9. 最新小白详细描述在centos7.5上安装python3并使用Nginx+virtualenv+supervisor来部署tornado项目(整理集合结合实际)系列1
  10. 硅谷华人码农成语大全
  11. js layui 模板属性 添加_layui模板引擎如何使用 - layim
  12. 三天打鱼两天晒网python程序_三天打鱼两天晒网
  13. 民办二本计算机专业学生出路何在?
  14. IJCAI 2022 | 用一行代码大幅提升零样本学习方法效果!南京理工牛津提出即插即用分类器模块...
  15. IT男把笔记本电脑放膝上会怎样?
  16. 连接远程电脑的时候提示:此计算机无法连接到远程计算机
  17. 博图用到c语言了吗,浅谈西门子TIA博图软件
  18. 做自媒体,有哪些免费下载视频剪辑素材的网站?
  19. 华为hicar支持车型列表_华为重申不造车:谁再建言造车调岗处分
  20. PHP基础-表单数据

热门文章

  1. 免费SSH账号申请网址
  2. NTP反射放大攻击(三)ntp.conf配置文件详解
  3. java 流计算_JAVA流式计算
  4. 流媒体视频网站Go语言实现
  5. 使用fixed进行表格列冻结之后出现的问题
  6. 爬山算法matlab程序,爬山算法和模拟退火算法
  7. 垂直翻页的Viewpager 兼容华为手机
  8. Wannafly挑战赛10 C:小H和游戏
  9. HTML5响应式手机模板:电商网站设计——仿淘宝手机app界面模板源码 HTML+CSS+JavaScript
  10. 芋道源码的周天(2018.06.06)