利用openfire和smark的即时通信

本文详细介绍如何使用 Smack 库实现 XMPP 即时通讯功能,包括建立连接、发送消息、管理好友列表、文件传输及多人聊天室的创建与管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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


下面是创建一个连接

	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中设置


发送消息

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);
	}

发送离线消息

	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());
		}
	}

得到好友列表

	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]
		 */
	}

管理好友,监听好友请求

<pre name="code" class="java">

</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类的使用

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();
		 }
	}

设置自己的状态信息

	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);
	}

监听好友的状态

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");
			}
		});
	}

监听好友的输入状态

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);
	}

加入聊天室进行多人聊天

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();
			}
		}
	}
	

发送和接收文件

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());
	}
创建多人聊天室

	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");
	}

管理房间

<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");
	}


注册

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;
			}
		}
	}

管理账号密码

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.youkuaiyun.com/shimiso/article/details/11225873



评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值