No more than code.
使用 Openfire 和 Strophe 构建基于 XMPP 的 IM 示例
名词解释
XMPP:Extensible Messaging and Presence Protocol,前称Jabber,是一种以XML为基础的开放式实时通信协议。
Openfire:是开源的,基于XMPP,采用Java开发的即时通讯服务器。
Strophe:一个Javascript版的XMPP类库。
BOSH:Bidirectional-streams Over Synchronous HTTP,为双向同步数据提供一个模拟层,借助这个标准可以建立一个较长的HTTP连接,当有新数据时,马上返回数据并关闭,否则请求失效。无论哪种情况,都会重新建立一个请求。
插件
"strophe.js": "^1.3.5"
"strophejs-plugin-rsm": "0.0.2"
demo
// Openfire.js
import { Strophe } from 'strophe.js';
// XMPP服务器BOSH地址
var BOSH_SERVICE = 'http://0.0.0.0:7070/http-bind/';
// XMPP连接
var connection = null;
// 当前状态是否连接
var connected = false;
// 存放添加好友请求
var addFriend_warn = []
var SERVER_DOMAIN = "ance.yixin.dk";
// 登陆
function login(username, password) {
if (!connected) {
connection = new Strophe.Connection(BOSH_SERVICE);
console.log("login -> new Strophe", connection)
connection.connect(username + `@${SERVER_DOMAIN}`, password, onConnect);
}
}
// 接收到<message>
function onMessage(msg) {
// 解析出<message>的from、type属性,以及body子元素
var from = msg.getAttribute('from');
// var from = msg.getAttribute('from').match(/(\S*)@/)[1];
var type = msg.getAttribute('type');
var elems = msg.getElementsByTagName('body');
if (type == "chat" && elems.length > 0) {
var body = elems[0];
var text = Strophe.getText(body) // 文本
console.log("onMessage -> text", text)
if (text.search('data:image') !== -1) { // base64图片
imReceiveMsg(text, "img")
} else {
imReceiveMsg(text, "text")
}
}
return true;
}
// 发送
function sendMsg(otherId, myselfId, message) {
if (connected) {
var msg = $msg({
to: otherId + `@${SERVER_DOMAIN}`,
from: myselfId + `@${SERVER_DOMAIN}`,
type: 'chat',
xmlns: "jabber:client"
}).c("body", null, message);
connection.send(msg.tree());
connection.send($pres().tree());
}
}
// 发送前需要将图片转换成bese64
function sendImg(otherId, myselfId, message) {
if (connected) {
var msg = $msg({
to: otherId + `@${SERVER_DOMAIN}`,
from: myselfId + `@${SERVER_DOMAIN}`,
type: 'chat',
xmlns: "jabber:client"
}).c("body", null, message);
connection.send(msg.tree());
connection.send($pres().tree());
}
}
function onConnect(status) {
console.log(status)
console.log(Strophe.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) {
console.log('连接成功')
connected = true;
// getFriends()
// 当接收到<message>节,调用onMessage回调函数
connection.addHandler(onMessage, null, 'message', null, null, null);
connection.addHandler(on_subscription_request, null, "presence", "subscribe");
// 首先要发送一个<presence>给服务器(initial presence)
connection.send($pres().tree());
}
}
// 查询好友列表
function findFriends() {
var friends = [];
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];
friends.push(jid)
});
});
return friends
}
// 添加好友
function getFriend(name) {
console.log("getFriend -> name", name)
connection.send($pres({ to: name + `@${SERVER_DOMAIN}`, 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));
// imReceiveMsg(addFriend_warn, "friends")
}
// 接受邀请
function on_subscription_response(name) {
connection.send($pres({ to: name + `@${SERVER_DOMAIN}`, type: "subscribed" }));
}
// 拒绝邀请
function on_subscription_reject(name) {
connection.send($pres({ to: name + `@${SERVER_DOMAIN}`, type: "unsubscribed" }));
}
// 删除好友
function on_subscription_delete(name) {
connection.send($pres({ to: name + `@${SERVER_DOMAIN}`, type: "unsubscribe" }));
}
export default {
login,
sendMsg,
sendImg,
findFriends,
getFriend,
on_subscription_response,
on_subscription_request,
on_subscription_reject,
addFriend_warn,
on_subscription_delete
}
<template>
<div class='test'>
<!-- 登录 -->
<Row> <span>账号:</span>
<Input v-model="userId" placeholder="Enter something..." style="width: 300px" /></Row>
<Row class="marginTop10"><span>密码:</span>
<Input v-model="password" placeholder="Enter something..." style="width: 300px" /></Row>
<!-- 选择聊天对象 -->
<Row class="marginTop10"><span>选择好友:</span>
<RadioGroup v-model="chooseFriend">
<Radio v-for="(ele,index) in friendsList" :key="ele" :label="ele"></Radio>
</RadioGroup>
</Row>
<Button class="marginTop10" type="primary" @click="login">登录</Button>
<Button class="marginTop10" type="primary" @click="getFriends">获取好友列表</Button>
<div class="imgchoose">
<Button class="marginTop10 imgButton" type="primary">选择图片</Button>
<input class="imgInput" type="file" @change="sendImgMsg">
</div>
<Input class="marginTop10" v-model="msgInfo" type="textarea" :rows="4" placeholder="Enter something..." />
<Button class="marginTop10" type="primary" @click="sendMsg">发送</Button>
<!--聊天对话框-->
<ul class="chatwrap">
<li :class="{'right':ele.from=='r' }" v-for="(ele,index) in chatList" :key="index">
<span></span>
<p></p>
<img :src="ele.img" alt="" @click="bigImg(ele.img)">
</li>
</ul>
<!-- 查看大图 -->
<Modal v-model="showBigImg" title="查看图片" @on-cancel="showBigImg=false" footer-hide width="800">
<img :src="selectImg" alt="" @click="bigImg(ele.img)">
</Modal>
</div>
</template>
<script>
import IM from "./openfire"
export default {
name: 'test',
data() {
return {
friendsList: [],
dataSource: [
{ key: "订单编号", value: "123121312" },
{ key: "订单编号", value: "123121312" },
{ key: "订单编号", value: "123121312" },
{ key: "订单编号", value: "大口径阿萨德那是扩大绿色大使,dasd 马拉松的马路上的马上到" }
],
selectImg: "",
showBigImg: false,
userId: "",
password: "",
msgInfo: "",
chooseFriend: "",
chatList: [{
text: "你好啊",
from: "l",
time: "2012-05-20",
type: "msg"
}]
};
},
mounted() {
window.imReceiveMsg = this.imReceiveMsg;
window.getFriends = this.getFriends;
window.login = this.login;
// IM.login("abc", "abc")
},
methods: {
// 获取好友列表
getFriends() {
this.friendsList = IM.findFriends();
},
// 登录
login() {
IM.login(this.userId, this.password)
},
// 发送文字消息
sendMsg() {
this.chatList.push({
text: this.msgInfo,
from: "r",
time: "2012-05-20"
})
IM.sendMsg(this.chooseFriend, this.userId, this.msgInfo)
},
// 接收消息
imReceiveMsg(text, type) {
if (type == "img") {
this.chatList.push({
img: text,
from: "l",
time: "2012-05-20"
})
} else {
this.chatList.push({
text: text,
from: "l",
time: "2012-05-20"
})
}
},
// 发送图片消息
sendImgMsg(e) {
// 文件转base64
var reader = new FileReader()
reader.readAsDataURL(e.target.files[0])
let _this = this;
reader.onload = function(e) {
_this.chatList.push({
img: reader.result,
from: "r",
time: "2012-05-20"
})
IM.sendMsg(_this.chooseFriend, _this.userId, reader.result)
}
},
// 查看大图
bigImg(src) {
this.selectImg = src;
this.showBigImg = true;
}
},
}
</script>
<style lang='less' scoped>
.test {
padding: 10px;
.marginTop10 {
margin-top: 10px;
}
.chatwrap {
margin-top: 30px;
.right {
text-align: right;
}
img {
width: 100px;
}
}
.imgchoose {
position: relative !important;
.imgInput {
position: absolute !important;
left: 0;
top: 10px;
height: 30px;
opacity: 0;
width: 80px;
}
}
.orderTemplate {
width: 400px;
padding: 10px;
background: #fff;
}
}
</style>