一直想写东西,但是上班不能在CSDN上写东西。下班回家又忙着学Iphone开发,唉,两个个字,忒忙。今天就写写Smack的聊天室功能吧。
先上代码:
- import java.io.BufferedReader;
- import java.io.InputStreamReader;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Iterator;
- import java.util.List;
- import org.jivesoftware.smack.Chat;
- import org.jivesoftware.smack.ConnectionConfiguration;
- import org.jivesoftware.smack.MessageListener;
- import org.jivesoftware.smack.PacketListener;
- import org.jivesoftware.smack.SmackConfiguration;
- import org.jivesoftware.smack.XMPPConnection;
- import org.jivesoftware.smack.XMPPException;
- import org.jivesoftware.smack.packet.Message;
- import org.jivesoftware.smack.packet.Packet;
- import org.jivesoftware.smack.provider.ProviderManager;
- import org.jivesoftware.smackx.Form;
- import org.jivesoftware.smackx.FormField;
- import org.jivesoftware.smackx.ServiceDiscoveryManager;
- import org.jivesoftware.smackx.muc.DefaultParticipantStatusListener;
- import org.jivesoftware.smackx.muc.DefaultUserStatusListener;
- import org.jivesoftware.smackx.muc.DiscussionHistory;
- import org.jivesoftware.smackx.muc.HostedRoom;
- import org.jivesoftware.smackx.muc.InvitationListener;
- import org.jivesoftware.smackx.muc.InvitationRejectionListener;
- import org.jivesoftware.smackx.muc.MultiUserChat;
- import org.jivesoftware.smackx.muc.RoomInfo;
- import org.jivesoftware.smackx.muc.SubjectUpdatedListener;
- import org.jivesoftware.smackx.packet.ChatStateExtension;
- import org.jivesoftware.smackx.packet.DiscoverInfo;
- import org.jivesoftware.smackx.packet.DiscoverItems;
- import org.jivesoftware.smackx.packet.OfflineMessageInfo;
- import org.jivesoftware.smackx.packet.OfflineMessageRequest;
- import org.jivesoftware.smackx.provider.AdHocCommandDataProvider;
- import org.jivesoftware.smackx.provider.BytestreamsProvider;
- import org.jivesoftware.smackx.provider.DataFormProvider;
- import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
- import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
- import org.jivesoftware.smackx.provider.IBBProviders;
- import org.jivesoftware.smackx.provider.MUCAdminProvider;
- import org.jivesoftware.smackx.provider.MUCOwnerProvider;
- import org.jivesoftware.smackx.provider.MUCUserProvider;
- import org.jivesoftware.smackx.provider.StreamInitiationProvider;
- import org.jivesoftware.smackx.provider.VCardProvider;
- import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;
- public class TestSmack2 {
- public static void main(String[] args) {XMPPConnection.DEBUG_ENABLED = true;
- final ConnectionConfiguration connectionConfig = new ConnectionConfiguration("PC2010102716", 5222, "");
- connectionConfig.setSASLAuthenticationEnabled(false);
- ProviderManager pm = ProviderManager.getInstance();
- configure(pm);
- try {
- XMPPConnection connection = new XMPPConnection(connectionConfig);
- connection.connect();//连接
- initFeatures(connection);
- connection.login("test", "test");//登陆
- //聊天室
- //MultiUserChat multiUserChat = new MultiUserChat(connection, new InvitationListener() {});
- //查找服务
- System.out.println(connection.getServiceName());
- List<String> col = getConferenceServices(connection.getServiceName(), connection);
- for (Object aCol : col) {
- String service = (String) aCol;
- //查询服务器上的聊天室
- Collection<HostedRoom> rooms = MultiUserChat.getHostedRooms(connection, service);
- for(HostedRoom room : rooms) {
- //查看Room消息
- System.out.println(room.getName() + " - " +room.getJid());
- RoomInfo roomInfo = MultiUserChat.getRoomInfo(connection, room.getJid());
- if(roomInfo != null) {
- System.out.println(roomInfo.getOccupantsCount() + " : " + roomInfo.getSubject());
- }
- }
- }
- /*---创建默认配置的聊天室 ---
- 先看看官方的文档:
- Creates a new multi user chat with the specified connection and room name. Note: no
- * information is sent to or received from the server until you attempt to
- * {@link #join(String) join} the chat room. On some server implementations,
- * the room will not be created until the first person joins it
- * 最重要一句:直到用户调用join方法的时候聊天室才会被创建
- */
- MultiUserChat muc = new MultiUserChat(connection, "instant@conference.pc2010102716");
- muc.create("user1");
- muc.sendConfigurationForm(new Form(Form.TYPE_SUBMIT));
- //----创建手动配置聊天室----
- muc = new MultiUserChat(connection, "reserved4@conference.pc2010102716");
- //销毁聊天室
- //muc.destroy("Test", null);
- muc.create("user2");
- //获取聊天室的配置表单
- Form form = muc.getConfigurationForm();
- //根据原始表单创建一个要提交的新表单
- Form submitForm = form.createAnswerForm();
- //向提交的表单添加默认答复
- for(Iterator<FormField> fields = form.getFields(); fields.hasNext();) {
- FormField field = (FormField) fields.next();
- if(!FormField.TYPE_HIDDEN.equals(field.getType()) && field.getVariable() != null) {
- submitForm.setDefaultAnswer(field.getVariable());
- }
- }
- //重新设置聊天室名称
- submitForm.setAnswer("muc#roomconfig_roomname", "Reserved4 Room");
- //设置聊天室的新拥有者
- List<String> owners = new ArrayList<String>();
- owners.add("test@pc2010102716");
- submitForm.setAnswer("muc#roomconfig_roomowners", owners);
- //设置密码
- submitForm.setAnswer("muc#roomconfig_passwordprotectedroom", true);
- submitForm.setAnswer("muc#roomconfig_roomsecret", "reserved");
- //设置描述
- submitForm.setAnswer("muc#roomconfig_roomdesc", "新创建的reserved聊天室");
- //设置聊天室是持久聊天室,即将要被保存下来
- //submitForm.setAnswer("muc#roomconfig_persistentroom", true);
- //发送已完成的表单到服务器配置聊天室
- muc.sendConfigurationForm(submitForm);
- //加入聊天室(使用昵称喝醉的毛毛虫 ,使用密码ddd)
- muc = new MultiUserChat(connection, "ddd@conference.pc2010102716");
- muc.join("喝醉的毛毛虫", "ddd");
- //监听消息
- muc.addMessageListener(new PacketListener() {
- @Override
- public void processPacket(Packet packet) {
- Message message = (Message) packet;
- System.out.println(message.getFrom() + " : " + message.getBody());;
- }
- });
- //muc = new MultiUserChat(connection, "ddd@conference.pc2010102716");
- //muc.join("喝醉的毛毛虫", "ddd");
- //加入聊天室(使用昵称喝醉的毛毛虫 ,使用密码ddd)并且获取聊天室里最后5条信息,
- //注:addMessageListener监听器必须在此join方法之前,否则无法监听到需要的5条消息
- muc = new MultiUserChat(connection, "ddd@conference.pc2010102716");
- DiscussionHistory history = new DiscussionHistory();
- history.setMaxStanzas(5);
- muc.join("喝醉的毛毛虫", "ddd", history, SmackConfiguration.getPacketReplyTimeout());
- //监听拒绝加入聊天室的用户
- muc.addInvitationRejectionListener(new InvitationRejectionListener() {
- @Override
- public void invitationDeclined(String invitee, String reason) {
- System.out.println(invitee + " reject invitation, reason is " + reason);
- }
- });
- //邀请用户加入聊天室
- muc.invite("test3@pc2010102716", "大家来谈谈人生");
- //监听邀请加入聊天室请求
- MultiUserChat.addInvitationListener(connection, new InvitationListener() {
- @Override
- public void invitationReceived(XMPPConnection conn, String room, String inviter, String reason, String password, Message message) {
- //例:直接拒绝邀请
- MultiUserChat.decline(conn, room, inviter, "你丫很闲啊!");
- }
- });
- //根据roomJID获取聊天室信息
- RoomInfo roomInfo = MultiUserChat.getRoomInfo(connection, "ddd@conference.pc2010102716");
- System.out.println(roomInfo.getRoom() + "-" + roomInfo.getSubject() + "-" + roomInfo.getOccupantsCount());
- //判断用户是否支持Multi-User聊天协议
- //注:需要加上资源标识符
- boolean supports = MultiUserChat.isServiceEnabled(connection, "test3@pc2010102716/spark");
- //获取某用户所加入的聊天室
- if(supports) {
- Iterator<String> joinedRooms = MultiUserChat.getJoinedRooms(connection, "test3@pc2010102716/spark");
- while(joinedRooms.hasNext()) {
- System.out.println("test3 has joined Room " + joinedRooms.next());
- }
- }
- //与聊天室用户私聊
- Chat chat = muc.createPrivateChat("ddd@conference.pc2010102716/飞鸟", new MessageListener() {
- @Override
- public void processMessage(Chat chat, Message message) {
- System.out.println("Private Chat: Received message from " + message.getFrom() + "-" + message.getBody());
- }
- });
- chat.sendMessage("今天不用加班吧?");
- //改变聊天室主题
- muc.addSubjectUpdatedListener(new SubjectUpdatedListener() {
- @Override
- public void subjectUpdated(String subject, String from) {
- System.out.println("Subject updated to " + subject +" by " + from);
- }
- });
- //muc.changeSubject("New Subject11");
- /*一个成员可能有四种角色:
- 1:主持者(Moderator) (权限最大的角色,管理其他成员在聊天室中的角色
- 2:参与者(Participant
- 3:游客 (Visitor) (不能向所有成员发送消息)
- 4:无(没有角色)(NONE)
- */
- /*聊天室用户可以有5种从属关系
- * 1、所有者 Owner
- * 2、管理员 Admin
- * 3、成员 Member
- * 4、被驱逐者 Outcast
- * 5、无(不存在从属关系) None
- */
- //配置聊天室为Moderated聊天室
- form = muc.getConfigurationForm();
- Form answerForm = form.createAnswerForm();
- answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
- muc.sendConfigurationForm(answerForm);
- //监听自己的状态变更和事件
- muc.addUserStatusListener(new DefaultUserStatusListener() {
- @Override
- public void voiceRevoked() {
- super.voiceRevoked();
- System.out.println("你被禁言了!");
- }
- @Override
- public void voiceGranted() {
- super.voiceGranted();
- System.out.println("你被批准发言了!");
- }
- @Override
- public void membershipGranted() {
- super.membershipGranted();
- System.out.println("你被赋予了Member权限");
- }
- @Override
- public void membershipRevoked() {
- super.membershipRevoked();
- System.out.println("你被解除了Member权限");
- }
- @Override
- public void adminGranted() {
- super.adminGranted();
- System.out.println("你被赋予了管理员权限");
- }
- @Override
- public void adminRevoked() {
- super.adminRevoked();
- System.out.println("你被解除了管理员权限");
- }
- //......
- });
- //房主(Owner)批准test3发言权
- muc.grantVoice("test3@pc2010102716");
- //监听他人状态变更
- muc.addParticipantStatusListener(new DefaultParticipantStatusListener() {
- @Override
- public void voiceGranted(String participant) {
- super.voiceGranted(participant);
- System.out.println(participant + "被批准发言了!");
- }
- @Override
- public void voiceRevoked(String participant) {
- super.voiceRevoked(participant);
- System.out.println(participant + "被禁言了!");
- }
- @Override
- public void membershipRevoked(String participant) {
- super.membershipRevoked(participant);
- }
- @Override
- public void adminGranted(String participant) {
- super.adminGranted(participant);
- }
- @Override
- public void adminRevoked(String participant) {
- super.adminRevoked(participant);
- }
- });
- //房主(Owner)批准test3管理员特权
- muc.grantAdmin("test3@pc2010102716");
- //发送消息
- BufferedReader cmdIn = new BufferedReader(new InputStreamReader(System.in));
- while(true) {
- try {
- String cmd = cmdIn.readLine();
- if("!q".equalsIgnoreCase(cmd)) {
- break;
- }
- }catch(Exception ex) {
- }
- }
- connection.disconnect();
- System.exit(0);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public static List<String> getConferenceServices(String server, XMPPConnection connection) throws Exception {
- List<String> answer = new ArrayList<String>();
- ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection);
- DiscoverItems items = discoManager.discoverItems(server);
- for (Iterator<DiscoverItems.Item> it = items.getItems(); it.hasNext();) {
- DiscoverItems.Item item = (DiscoverItems.Item)it.next();
- if (item.getEntityID().startsWith("conference") || item.getEntityID().startsWith("private")) {
- answer.add(item.getEntityID());
- }
- else {
- try {
- DiscoverInfo info = discoManager.discoverInfo(item.getEntityID());
- if (info.containsFeature("http://jabber.org/protocol/muc")) {
- answer.add(item.getEntityID());
- }
- }
- catch (XMPPException e) {
- }
- }
- }
- return answer;
- }
- private static void configure(ProviderManager pm) {
- // Service Discovery # Items
- pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
- // Service Discovery # Info
- pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
- // Service Discovery # Items
- pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
- // Service Discovery # Info
- pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
- //Offline Message Requests
- pm.addIQProvider("offline","http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider());
- //Offline Message Indicator
- pm.addExtensionProvider("offline","http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider());
- //vCard
- pm.addIQProvider("vCard","vcard-temp", new VCardProvider());
- //FileTransfer
- pm.addIQProvider("si","http://jabber.org/protocol/si", new StreamInitiationProvider());
- pm.addIQProvider("query","http://jabber.org/protocol/bytestreams", new BytestreamsProvider());
- pm.addIQProvider("open","http://jabber.org/protocol/ibb", new IBBProviders.Open());
- pm.addIQProvider("close","http://jabber.org/protocol/ibb", new IBBProviders.Close());
- pm.addExtensionProvider("data","http://jabber.org/protocol/ibb", new IBBProviders.Data());
- //Data Forms
- pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
- //Html
- pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider());
- //Ad-Hoc Command
- pm.addIQProvider("command", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider());
- // Chat State
- ChatStateExtension.Provider chatState = new ChatStateExtension.Provider();
- pm.addExtensionProvider("active", "http://jabber.org/protocol/chatstates", chatState);
- pm.addExtensionProvider("composing", "http://jabber.org/protocol/chatstates",
- chatState);
- pm.addExtensionProvider("paused", "http://jabber.org/protocol/chatstates", chatState);
- pm.addExtensionProvider("inactive", "http://jabber.org/protocol/chatstates", chatState);
- pm.addExtensionProvider("gone", "http://jabber.org/protocol/chatstates", chatState);
- //MUC User,Admin,Owner
- pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user", new MUCUserProvider());
- pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin", new MUCAdminProvider());
- pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner", new MUCOwnerProvider());
- }
- private static void initFeatures(XMPPConnection xmppConnection) {
- ServiceDiscoveryManager.setIdentityName("Android_IM");
- ServiceDiscoveryManager.setIdentityType("phone");
- ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(xmppConnection);
- if(sdm == null) {
- sdm = new ServiceDiscoveryManager(xmppConnection);
- }
- sdm.addFeature("http://jabber.org/protocol/disco#info");
- sdm.addFeature("http://jabber.org/protocol/caps");
- sdm.addFeature("urn:xmpp:avatar:metadata");
- sdm.addFeature("urn:xmpp:avatar:metadata+notify");
- sdm.addFeature("urn:xmpp:avatar:data");
- sdm.addFeature("http://jabber.org/protocol/nick");
- sdm.addFeature("http://jabber.org/protocol/nick+notify");
- sdm.addFeature("http://jabber.org/protocol/xhtml-im");
- sdm.addFeature("http://jabber.org/protocol/muc");
- sdm.addFeature("http://jabber.org/protocol/commands");
- sdm.addFeature("http://jabber.org/protocol/si/profile/file-transfer");
- sdm.addFeature("http://jabber.org/protocol/si");
- sdm.addFeature("http://jabber.org/protocol/bytestreams");
- sdm.addFeature("http://jabber.org/protocol/ibb");
- sdm.addFeature("http://jabber.org/protocol/feature-neg");
- sdm.addFeature("jabber:iq:privacy");
- }
- }
上面有两张Spark客户端的聊天室列表占有者一列不同的原因:当使用以下代码获取时不能获取occupantsCount和subject的值:
- System.out.println(roomInfo.getOccupantsCount() + " : " + roomInfo.getSubject());
这是由于Openfire端有个bug(暂且这样说吧,我不知为什么Openfire这样做).首先修改Smack的一个bug,修改RoomInfo类的RoomInfo(DiscoverInfo info) 方法:
- Iterator<String> values = form.getField("muc#roominfo_subject").getValues();
- if (values.hasNext()) {
- this.subject = values.next();
- }
- else {
- this.subject = "";
- }
改为:
- final FormField subjField = form.getField("muc#roominfo_subject");
- this.subject = subjField == null ? "" : subjField.getValues().next();
修改Openfire的源码,org/jivesoftware/openfire/muc/spi/MultiUserChatServiceImpl类的public DataForm getExtendedInfo(String name, String node, JID senderJID) 方法:
- /*final FormField fieldOcc = dataForm.addField(); */
- fieldSubj.setVariable("muc#roominfo_occupants");
- fieldSubj.setLabel(LocaleUtils.getLocalizedString("muc.extended.info.occupants"));
- fieldSubj.addValue(Integer.toString(room.getOccupantsCount()));
改为:
- final FormField fieldOccu= dataForm.addField();
- fieldOccu.setVariable("muc#roominfo_occupants");
- fieldOccu.setLabel(LocaleUtils.getLocalizedString("muc.extended.info.occupants"));
- fieldOccu.addValue(Integer.toString(room.getOccupantsCount()));
使用asmack开发基于Android的IM系统同理.