参考文章:
javascript - 在基于 Strophe .js聊天应用程序中处理状态 - 堆栈溢出 (stackoverflow.com)
javascript - 在 Strophe js 中处理存在 - 堆栈溢出 (stackoverflow.com)
Space/XMPP文档列表/XMPP扩展/XEP-0166 - Jabber/XMPP中文翻译计划 (jabbercn.org)
本文只是参考官方文档后的个人理解希望可以帮助到刚开始使用或者以后会用的strophe.js的小伙伴
引用JS:
<script type="text/javascript" src="http://cdn.bootcss.com/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/strophe.js/1.1.3/strophe.min.js"></script>
先上代码:
demo.html页面:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<script src='../js/jquery.js'></script>
<script src='../js/strophe.js'></script>
<script src='../js/pages/demo.js'></script>
</head>
<!-- -->
<body>
JID:<input type="text" id="input-jid" value="10021@hthg.chat">
<br>
密码:<input type="password" id="input-pwd" value="10021@JcrdOA.chat">
<br>
<button id="btn-login">登录</button>
<div id="msg" style="height: 400px; width: 400px; overflow: scroll;"></div>
联系人JID:
<input type="text" id="input-contacts">
<br>
消息:
<br>
<textarea id="input-msg" cols="30" rows="4"></textarea>
<br>
<button id="btn-send">发送</button>
<button id="friend">好友列表</button>
<div id="friendlist" style="height: 400px; width: 400px; overflow: scroll;"></div>
<button id="getUserInfo">获取个人信息</button>
<button id="getGroup">获取群组</button>
<div id="userInfo" style="height: 400px; width: 400px; overflow: scroll;"></div>
</body>
</html>
demo.js页面:
//这里写自己的服务器地址
var BOSH_SERVICE = 'http://*******/http-bind';
// XMPP连接
var connection = null;
// 当前状态是否连接
var connected = false;
// 当前登录的JID
var jid = "";
//房间名称
var ROOM_JID='';//注意格式!! <room@service/nick>
// 连接状态改变的事件
function onConnect(status) {
console.log('status: ' + status)
if (status == Strophe.Status.CONNFAIL) {
alert("连接失败!");
} else if (status == Strophe.Status.AUTHFAIL) {
alert("登录失败!");
} else if (status == Strophe.Status.DISCONNECTED) {
alert("连接断开!");
connected = false;
} else if (status == Strophe.Status.CONNECTED) {
alert("连接成功,可以开始聊天了!");
console.log('pubsub',connection)
connected = true;
// 当接收到<message>节,调用onMessage回调函数。一旦创建了连接,您需要定义钩子来接收消息并能够与其交互
connection.addHandler(onMessage, null, null, null, null, null);
// 首先要发送一个<presence>给服务器(initial presence)
connection.send($pres().tree());
//获取订阅的主题信息
connection.pubsub.getSubscriptions(onMessage,5000);
}
}
// 接收到<message>(这里接收消息不仅仅是私聊接收消息 主要可以用来与服务端进行互动可以看到控制台服务端返回的消息节)
function onMessage(msg) {
console.log('--- msg ---', msg);
// 解析出<message>的from、type属性,以及body子元素
var from = msg.getAttribute('from');
var type = msg.getAttribute('type');
var elems = msg.getElementsByTagName('body');
if (type == "chat" && elems.length > 0) {
var body = elems[0];
$("#msg").append(from + ":<br>" + Strophe.getText(body) + "<br>")
}
return true;
}
// 获取好友列表
function findFriends() {
var friendsid = [];
var friendsname = [];
var iq = $iq({ type: 'get' }).c('query', { xmlns: 'jabber:iq:roster' });
connection.sendIQ(iq, function (a) {
$(a).find('item').each(function () {
var jid = $(this).attr('jid').match(/(\S*)@/)[1];
var name = $(this).attr('name')
friendsid.push(jid)
$("#friendlist").append(jid+name + "<br>")
friendsname.push(name)
});
});
return friendsname;
}
// 获取个人信息(当前登录用户信息),获取好友信息和获取个人信息的用法是一样的,获取好友信息时发送iq节时to:"",to属性里传的是好友的JID,个人信息to属性里传的是当前登录用户的JID
function getUserInfo(callback){//callback方法是用于返回参数的方法具体可以查询strophe.js官方文档查看(在本demo里用不到此方法)
var userinfo=[];
var iq = $iq({ type: 'get' }).c('vCard', { xmlns: 'vcard-temp' });
connection.sendIQ(iq, function (a) {//大括号里的内容是对服务返回的个人信息的处理这里自行修改
var name=$(a).find('NICKNAME').text(); //姓名
var department=$(a).find('ORG').text(); //部门
var email=$(a).find('EMAIL');
var emailwork=email[0].textContent; //邮箱
var tel=$(a).find("TEL");
var workNumber=tel[0].textContent; //办公座机
var signature=tel[4].textContent; //个性签名
var homeNumber=tel[7].textContent; //手机号码
userinfo.push({
'name':name,
'department':department,
'email':emailwork,
'workNumber':workNumber,
'signature':signature,
'homeNumber':homeNumber,
})
//callback("success", {
//userinfo: userinfo,
//});
//下面注释这部分↓↓↓↓↓↓↓就是获取好友信息的用法
//var iq = $iq({
//type: 'get',
//to:friendJID,
//}).c('vCard', {
//xmlns: 'vcard-temp'
//});
//connection.sendIQ(iq, function(a) {
//console.log(a)
//}
})
return userinfo
}
// 获取群组列表
function findGroups() {
var groups = [];
var iq = $iq({ type: 'get' }).c('usergroup', { xmlns: 'http://www.jivesoftware.org/protocol/usergroup'});
connection.sendIQ(iq, function (a) {
$(a).find('group').each(function () {
if(this.innerHTML.indexOf("group_")!==-1){
var group = this.innerHTML;
groups.push(group)
}
});
});
return groups;
}
// 添加好友
function getFriend(name) {
connection.send($pres({ to: name + '@openfire.cn', type: "subscribe" }));
}
// 收到好友请求
function on_subscription_request(stanza) {
if (stanza.getAttribute("type") == "subscribe") {
var from = stanza.getAttribute("from").match(/(\S*)@/)[1]
addFriend_warn.push(from)
}
addFriend_warn = Array.from(new Set(addFriend_warn));
}
// 接受邀请
function on_subscription_response(name) {
connection.send($pres({ to: name + '@openfire.cn', type: "subscribed" }));
}
// 拒绝邀请
function on_subscription_reject(name) {
connection.send($pres({ to: name + '@openfire.cn', type: "unsubscribed" }));
}
// 删除好友
function on_subscription_delete(name) {
connection.send($pres({ to: name + '@openfire.cn', type: "unsubscribe" }));
}
$(document).ready(function() {
//点击登录 通过BOSH连接XMPP服务器
$('#btn-login').click(function() {
if(!connected) {
console.log('jid: ' + $("#input-jid").val());
console.log('pwd: ' + $("#input-pwd").val());
connection = new Strophe.Connection(BOSH_SERVICE);
connection.connect($("#input-jid").val(), $("#input-pwd").val(), onConnect);
jid = $("#input-jid").val();
}
});
// 发送消息(私聊)
$("#btn-send").click(function() {
if(connected) {
if($("#input-contacts").val() == '') {
alert("请输入联系人!");
return;
}
// 创建一个<message>元素并发送
var msg = $msg({
to: $("#input-contacts").val(),
from: jid,
type: 'chat'
}).c("body", null, $("#input-msg").val());
connection.send(msg.tree());
$("#msg").append(jid + ":<br>" + $("#input-msg").val() + "<br>");
$("#input-msg").val('');
} else {
alert("请先登录!");
}
});
// 发送消息(群聊)
$("#btn-send").click(function() {
if(connected) {
// 创建一个<message>元素并发送
var msg = $msg({
to: ROOM_JID,
from: jid,
type: 'groupchat'
}).c("body", null, $("#input-msg").val());
connection.send(msg.tree());
$("#input-msg").val('');
} else {
alert("请先登录!");
}
});
// 获取好友列表
$("#friend").click(function() {
if(connected) {
console.log(findFriends())
}else{
alert("请先登录!");
}
})
// 获取个人信息
$("#getUserInfo").click(function() {
if(connected) {
console.log(getUserInfo())
getUserInfo()
}else{
alert("请先登录!");
}
})
// 获取群组
$("#getGroup").click(function() {
if(connected) {
console.log(findGroups())
}else{
alert("请先登录!");
}
})
});
获取群组列表:获取群组列表这里有两种方法
方法一、就是代码里的方法↑
方法二、参考官方文档↓
<iq from='hag66@shakespeare.lit/pda'
id='disco2'
to='macbeth.shakespeare.lit'
type='get'>
<query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>
上面这个这个iq节↑↑↑↑↑↑↑↑↑是要向服务端发送的内容,然后服务端会自动返回群组列表(所以它只是一个服务端能够认识的一个格式)但是如何发送这种格式?
就是使用下面这个方法↓↓↓↓↓↓↓↓sendIQ()方法发送,var iq变量里就是配置上面↑这个iq节里的元素所需的参数(所以这两个东西其实就是一个东西)
我在网上查到的大部分资料基本上都是上面↑这种iq元素的格式却不明白如何发送这种格式所以文章后面我会重点说明一下如何发送这种格式
//发现房间
function findRoom() {
if (connected) {
var iq = $iq({
to:'', //to这里注意格式
type: 'get',
}).c('query', {
xmlns: 'http://jabber.org/protocol/disco#items'
});
connection.sendIQ(iq, function(a) {
console.log(a)
})
} else {
alert("请登录")
}
}
说一下这个方法↑↑↑↑ to:' ',to里的参数也就是to='macbeth.shakespeare.lit'这个参数指的就是Openfire里服务名和你当前命名的聊天的服务名的组合(to这个参数可以理解为发送给谁,所以可以是一个用户JID也可以是一个服务地址)。其他的参数就按照官方文档里给的格式自己去配置就好,id参数可以忽略服务端会自动生成,from参数里就是当前登录的JID也可以不写,服务也会自动生成
因为我这里命名的是conference.hthg.chat所以我这里就得写to:"conference.hthg.chat" 但是要注意这里查询群组列表的时候不加@符号,当然后面创建群组新建房间时也会用到这个命名但是在创建房间时需要加@符号
这里说一下这个命名是怎么来的登录Openfire我们可以看到:
所以也就是聊天服务名和服务器名的组合
其他私聊接收消息、发送消息、获取好友列表的方法基本和获取群组列表差不多都是一个路子自己能理解就行
做到这里就可以实现私聊发送消息的功能 但是下面就是我要说的一个重点如何发送群聊消息
群聊发送消息和私聊不太一样先说一下我的思路:
私聊只需要一个用户对另一个用户发送一个<message>消息节就可以实现。但是群聊需要多个小功能拼凑才能实现比如:如果要发起群聊首先需要当前用户创建一个房间,并且当前用户需要在这个新房间里(加入这个新房间),并且当前用户还得是这个房间的群主(房间拥有者),然后必须要配置房间的参数包括分配人员权限等(比如:房间最大人数、房间是否是永久性、是否需要输入密码、设置密码、是否可以群内成员邀请其他成员加入本群等等)。有了这些之后就可以邀请成员加入本群了。房间创建成功后和私聊不同的是发送消息时,私聊是对用户发送消息(to:'JID',to属性里是一个用户的JID),但是群聊就变成对这个房间发送消息(to:'ROOM_JID',to属性里就是房间名称)。
参考官方文档:
//例子 143. Jabber用户新建一个房间并声明对多用户聊天的支持
//先发向服务器送以下格式 用来创建房间(需要用js自己写发送方法 对应下面newRoom()方法)
<presence
from='crone1@shakespeare.lit/desktop'
to='darkcave@chat.shakespeare.lit/firstwitch'>
<x xmlns='http://jabber.org/protocol/muc'/>
</presence>
//发送格式后如果创建成功控制台<message>节会自动返回以下格式(代码里不需要写)
<presence
from='darkcave@chat.shakespeare.lit/firstwitch'
to='crone1@shakespeare.lit/desktop'>
<x xmlns='http://jabber.org/protocol/muc#user'>
<item affiliation='owner'
role='moderator'/>
<status code='110'/>
<status code='201'/>
</x>
</presence>
//如果创建不成功会返回error,比如以下格式(这只是其中一种error格式)
<presence
from='darkcave@chat.shakespeare.lit/thirdwitch'
to='hag66@shakespeare.lit/pda'
type='error'>
<x xmlns='http://jabber.org/protocol/muc'/>
<error type='cancel'>
<not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</presence>
//例子 18. Jabber用户准备进入一个房间(Multi-User Chat)
//向服务器发送以下格式,用于当前用户加入创建的新房间(需要用js自己写发送方法 对应下面newRoom()方法)
<presence
from="hag66@shakespeare.lit/pda"
to='darkcave@macbeth.shakespeare.lit/thirdwitch'>
<x xmlns='http://jabber.org/protocol/muc'/>
</presence>
根据格式发送出席信息、创建房间(顺序是当前用户先加入房间再创建房间再配置房间信息)
//群聊创建房间
function newRoom(groupname) {
if (connected) {
// 发送<presence>元素,加入房间
connection.send($pres({
from: jid,
to: groupname + "/" + jid.substring(0, jid.indexOf("@"))
}).c('x', { xmlns: 'http://jabber.org/protocol/muc' }).tree());
// 创建房间
var iq = $iq({
to: groupname,
type: 'get'
}).c("query", {
xmlns: 'http://jabber.org/protocol/muc#owner'
});
iq.c("x", {
xmlns: "jabber:x:data",
type: "submit"
});
// 房间配置参数
iq.c('field', { 'var': 'FORM_TYPE' }).c('value').t('http://jabber.org/protocol/muc#roomconfig').up().up();
iq.c('field', { 'var': 'muc#roomconfig_publicroom' }).c('value').t('1').up().up();// 让房间可以公开搜索
iq.c('field', { 'var': 'muc#roomconfig_persistentroom' }).c('value').t('1').up().up();// 设置房间持久性
iq.c('field', { 'var': 'muc#roomconfig_maxusers' }).c('value').t('100').up().up();// 设置房间最大人数
connection.sendIQ(iq.tree(), function(a) {
console.log(a)
});
} else {
alert("请登录")
}
}
配置房间信息写法格式就是这样,其他配置参数去官方文档查询后修改即可
这样就能完成创建房间 这里最好是分开写,两个步骤写在一个方法里看着会比较乱
说一下to:'',to属性里的格式
参考官方文档 这里创建房间时to属性里的格式是 <room@service/nick>
room:指的是房间名称也就是ROOM_JID。
@service:指的是服务器的聊天服务地址也就是之前↑↑↑↑↑↑查询群组列表所说的地址也就是@conference.hthg.chat ,注意这里的这个地址是要加@符号的,这个地址每个人设置的都不一样需要去登录Openfire查询。
nick:指的是当前用户加入到这个新房间后的名称
所以groupname变量里的格式为 room@service
我在创建房间时出现过的一个报错:
<error xmlns="jabber:client" code="404" type="cancel">
<remote-server-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
</error>
404这错误的原因就是在向服务器发送时to属性里的格式没有写对,按照room@service/nick这个格式去写即可
后续更新。。。。。