版权声明:本文为博主原创文章,未经博主允许不得转载。
服务器: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 plain copy 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中设置
发送消息
[html] view plain copy private void testSendMessage(XMPPConnection conn) throws Exception { //jid在数据表中ofroster可以查到,一般是 用户名@服务器名称 Chat chat = ChatManager.getInstanceFor(conn).createChat("ly@192.168.1.100", 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); } 发送离线消息
[java] view plain copy 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()); } } 得到好友列表
[java] view plain copy 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 : ly@192.168.1.100 type : from status : null ----------------------------- name : null user : yy@192.168.1.100 type : to status : null ----------------------------- [ly@192.168.1.100, yy@192.168.1.100] */ } 管理好友,监听好友请求
[java] view plain copy <pre name="code" class="java"> [java] view plain copy </pre><pre name="code" class="java">private void testAddAndDelFriends(final XMPPConnection conn) throws Exception { Roster r = conn.getRoster(); // 用户的jid,昵称,用户的分组。如果该用户不存在也可以添加 // r.createEntry("yy@192.168.1.100", "yy", null); // rosterEntry的构造方法是包访问权限,不能直接new // RosterEntry entry = r.getEntry("ly@192.168.1.100"); // 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类的使用
[java] view plain copy private void testGetFriendInfo(XMPPConnection conn) throws Exception { VCard vCard = new VCard(); VCardManager vcManager = new VCardManager(); //此处返回false boolean b = vcManager.isSupported("ly@192.168.1.100", conn); System.out.println(b); vCard.load(conn, "ly@192.168.1.100"); // 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(); } } 设置自己的状态信息
[java] view plain copy private void testSetInfo(XMPPConnection conn) throws Exception { VCard vCard = new VCard(); vCard.load(conn); vCard.setEmailHome("admin@126.com"); vCard.setAddressFieldWork("POSTAL", "汇宝大厦"); //修改完要保存修改的内容,否则没办法更新到服务器 vCard.save(conn); //修改自身的状态,包括隐身,上线(可以指定对特定的好友更改状态) Presence p = new Presence(Type.available); p.setTo("ly@192.168.1.100"); //修改心情 p.setStatus("我的心情"); //同样要发到服务器 conn.sendPacket(p); } 监听好友的状态
[java] view plain copy private void testSetRosterListener(XMPPConnection conn) throws Exception { Roster r = conn.getRoster(); r.createEntry("ly@192.168.1.100", "昵称", 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"); } }); } 监听好友的输入状态
[java] view plain copy private void testGetExtention(XMPPConnection conn) throws Exception { Chat chat = ChatManager.getInstanceFor(conn).createChat("ly@192.168.1.100", 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); } 加入聊天室进行多人聊天
[java] view plain copy 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("a@conference.192.168.1.100"); 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(); } } }
发送和接收文件
[java] view plain copy 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("admin@192.168.1.100/潘迪安"); File file = new File("D://time.jpg"); oft.sendFile(file, "图片"); System.out.println(oft.isDone()); } 创建多人聊天室
[java] view plain copy private void testCreateRoom(XMPPConnection conn) throws Exception { while(true) { if(conn != null) break; } //@之前的是会议房间名称,之后的是conference+ip(固定格式,不能改变) MultiUserChat muc = new MultiUserChat(conn, "ly@conference.192.168.1.100"); //昵称,如果该房间已经存在,则会抛出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("yy@192.168.1.100", "ly_room"); } 管理房间
[java] view plain copy <pre name="code" class="java">private void testManageRoom(XMPPConnection conn) throws Exception { testCreateRoom(conn); MultiUserChat muc = new MultiUserChat(conn, "ly@conference.192.168.1.100"); //Thread.sleep(5000); //赋予管理员权限 //muc.grantAdmin("yy@192.168.1.100"); //Thread.sleep(5000); //如果是管理员,则不能踢除 //muc.banUser("yy@192.168.1.100", "太水"); //收回说话的权限 muc.revokeVoice("yy"); //muc.grantVoice("yy"); }
注册
[java] view plain copy 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", "new00@126.com"); attributes.put("name", "name@192.168.1.100"); //添加用户,要设置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; } } }管理账号密码
[java] view plain copy 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