出自:http://blog.csdn.net/casuallc/article/details/34794501
服务器: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 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 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
[java] view plain copy
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 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 addresses) {
//该方法以及下面的方法都是在服务器修改好友信息时触发
System.out.println("entriesUpdated");
}
@Override
public void entriesDeleted(Collection addresses) {
// TODO Auto-generated method stub
System.out.println("entriesDeleted");
}
@Override
public void entriesAdded(Collection 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 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
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 attributes = new HashMap();
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 c = am.getAccountAttributes();
for(String s : c) {
System.out.println(s);
}
/*
* 通过accountManager可以得到的属性
* username
registered
name
password
*/
am.getAccountAttribute("username");
am.createAccount("newUser", "0");
am.changePassword("00");
}
至于细节和中间遇到的问题,在程序代码中都有叙述
参考博客: http://blog.csdn.net/shimiso/article/details/11225873