最近公司需要做一个简单的即时通讯功能,因为不是什么主要的功能板块,而且资金有限,所以要求要用开源的。之前自己用过环信还有极光,但是这两个都是要收费的。后面查了一段时间以后,选择用openfire作为服务端,strophe.js作为客户端来进行开发。主要实现了,文字、图片、视频、位置、文件发送,开发过程中遇到的问题还是蛮多的,这里就记录一下,以便以后参考!
首先是openfire服务器,服务器下载安装这里就不多说了,网上一搜一大堆,可以参考这篇文章:https://blog.51cto.com/favccxx/1618995
服务器搭建好了以后,需要几个东西:
1,服务器地址:http://zhangyh:7070/http-bind/
2,服务器名称:zhangyh
3,创建两个用户,用于后面进行聊天:admin和x5411
注意:用户创建这里有个小坑,不能用中文,不能用大写字母!
好了,现在服务端的东西都准备好了,剩下的就是客户端了,客户端这里,在选用strophe之前,试过converse.js和websocket,converse的话,用起来倒是挺方便的,但是如果你要自定义你自己的界面,想自己把各个模块分离出来的话超级麻烦,而且,它的开发文档,看起来只有那么操蛋了,直接pass掉。然后websocket的话,因为不是现成的,所有的包括连接,登录,发送,接收都要自己一步步的慢慢写,慢慢实现,用起来不是很方便。但是感觉如果想搞得好一点的话,使用websocket应该会好一点!最后,就选用了strophe。
开始客户端的开发:页面需要引入strophe.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>
1,连接openfire服务器,登录,注意:登录的用户名,后面需要拼上服务器的名称,如:admin@zhangyh
-
//登录openfire
-
function loginOpenfire() {
-
// 初始化strophe.js,获取当前账户的登录信息
-
let service = Constant.OPENFIRE_BOSH_SERVICE;
//服务器地址
-
window.connection =
new Strophe.Connection(service);
-
//刚才创建的用户的账号和密码
-
let userName = Constant.OPENFIRE_ACCOUNT_NAME;
//admin@zhangyh
-
let passWord = Constant.OPENFIRE_ACCOUNT_PASSWORD;
//admin@123
-
//开始登录,完成登录以后,会有个回调方法onConnect
-
connection.connect(userName, passWord,
function(status){
-
onConnect(status)
-
});
-
}
-
-
/**
-
* 连接状态改变时触发
-
*/
-
function onConnect(status) {
-
if (status == Strophe.Status.CONNFAIL) {
-
alert(
"连接失败,网络异常!");
-
}
else
if (status == Strophe.Status.AUTHFAIL) {
-
alert(
"登录失败,账号或密码错误!");
-
}
else
if (status == Strophe.Status.DISCONNECTED) {
-
alert(
"连接断开了!");
-
//这里可以根据自己的业务逻辑来处理,要保持登录就调用登录方法
-
loginOpenFire();
-
}
else
if (status == Strophe.Status.CONNECTED) {
-
console.log(
"连接成功,可以开始聊天了!");
-
// 当接收到消息时,调用reMessage回调函数
-
connection.addHandler(
function(msg){
-
reMessage(msg);
-
},
null,
'message',
null,
null,
null);
-
// 首先要发送一个<presence>给服务器(initial presence),这一步必须做,要不然服务器不知道你到底要干嘛
-
connection.send($pres().tree());
-
}
-
}
2,发送聊天信息
-
/**
-
* 发送聊天信息
-
*/
-
function sendText() {
-
var val =
"发送内容";
//需要发送的文字内容
-
// 创建一个<message>元素并发送
-
var msg = $msg({
-
to: sendTo,
//发送对象的用户名 x5411@zhangyh
-
from: jid,
//发送人 admin@zhangyh
-
type:
'chat'
-
}).c(
"body",
null, val);
-
connection.send(msg.tree());
-
console.log(msg.tree());
-
}
注意:这个地方,生成message树,创建body的时候,那个val如果是个字符串类型(文本内容),生成的body是这样的
<body>文本内容</body>
如果val是个对象的话,就会自动把对象的key和value以属性的形式放进body里面
3,接收聊天信息
-
/**
-
* 收到消息
-
*/
-
function reMessage(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];
-
alert(
"接收到的消息是:"+Strophe.getText(body));
-
}
-
return
true;
-
}
这里遇到个问题,我在写demo的时候,用的是原生的js写的,所以监听收到消息的触发,只需要在onConnect方法里面做一次就可以了,但是实际开发环境是用的react开发的,实际运行的时候接收消息的监听执行一次以后,后面就不会执行了,就导致正式聊天的时候第一次可以收到消息,后面就就接收不到了!百度到的一个比较接近的答案是,因为strophe.js里面在消息监听里面做了try{}catch{},然后正式环境里面进行消息监听的时候捕捉到了项目其它地方的异常信息,strophe以为消息监听异常没有完成,所以就销毁了这个监听(但是实际上是正常的)。感觉应该就是这个原因导致的,但是我把所有源码里面的异常捕捉都去掉了以后还是不行,所以就在每次监听完成接收到消息以后,再启用一次监听,这样即使上一个被销毁了,监听还是可以有效。
-
/**
-
* 收到消息
-
*/
-
function reMessage(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];
-
alert(
"接收到的消息是:"+Strophe.getText(body));
-
}
-
//每次收到消息以后都要进行下一次监听,如果不做这一步,就只能监听一次(未解决)
-
connection.addHandler(
function(msg){
-
reMessage(msg);
-
},
null,
'message',
null,
null,
null);
-
return
true;
-
}
上面就是一个简单的,登录、发送、接收的过程了!
然后问题又来了,strophe只用用于简单的文字聊天,涉及到文件的东西,需要借用到第三方的插件,XEP-0096: SI File Transfer,所以又去研究了一下文件传输的问题。研究了大半天,都没搞明白这东西是怎么传输的,光是文件传输的消息通知都没调通,一直报503(估计是我太菜了,有时间再继续研究下)。没办法,项目又催的比较急,只能另辟蹊径了!然后就想到了一个办法(这里不得不佩服一下我的机智了)。
这个办法是这样的,正常接收到的文字聊天信息是这样的<message><body>接收到消息了</body></message>,如果对方发送的是一个对象的话,比如 {name:123,type:456} ,那接收到的消息就是这样的<message><body name="123" type="456"></body></message>,所以,只要涉及到文件上传的,先将文件上传到我们自己的服务器上面,获取文件路径以后,将文件的所有信息以对象的形式发送给对方 ,对方接收到消息以后,根据body里面的type属性来区分发送方发送的是文字还是文件(图片,视频、位置同理)。
发送文件信息的完整过程:
1,选择文件
2,上传文件获取文件的完整路径、名称、大小等
3,将文件的信息封装成一个对象以文字的形式发送给接收方
4,这个时候接收方接收到消息以后,就可以进行解析,根据文件类型来进行展示
strophe和openfire这个东西实在太老了,网上描述倒是挺多的,但是真正开发的实例太少了,遇到问题都得自己慢慢摸索。过程虽然很坎坷,但是总算是搞完了!欢迎交流,一起进步!如果有什么说的不对的,欢迎指出哈!
转载:https://blog.csdn.net/qq_38400856/article/details/104535322