微信小程序> 小程序中如何实现即时通信聊天功能?

小程序中如何实现即时通信聊天功能?

浏览量:631 时间: 来源:Cherish_zzj

项目背景:小程序中实现实时聊天功能

 

一、服务器域名配置

配置流程

配置参考URL:https://developers.weixin.qq.com/miniprogram/dev/api/api-network.html

二、nginx中配置反向代理加密websocket(wss)

upstream websocket{hash $remote_addr consistent;server 127.0.0.1:9090 weight=5 max_fails=3 fail_timeout=30s;}server { listen  80;    server_name www.xxxx.cn;   rewrite ^(.*)$  https://$host$1 permanent;}server{ listen 443;    server_name www.xxxx.cn;    ssl on;    root  /home/wwwroot/yzcp;    index index.php index.html index.htm;    ssl_certificate   /usr/local/nginx/conf/cert/1526060965511.pem;#这里是服务端证书路径    ssl_certificate_key  /usr/local/nginx/conf/cert/1526060965511.key;#这里是密钥路径    ssl_session_timeout 5m;    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;    ssl_prefer_server_ciphers on;    ssl_verify_client off; #隐藏index.php        location / {#index index.php;deny 127.0.0.1;            if (!-e $request_filename) {               #一级目录               rewrite ^(.*)$ /index.php?s=$1 last;               break;            }            #wss配置            client_max_body_size 100m;            proxy_redirect off;            proxy_pass http://websocket;#反向代理转发地址            proxy_set_header Host $host;# http请求的主机域名            proxy_set_header X-Real-IP $remote_addr;# 远程真实IP地址            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;#反向代理之后转发之前的IP地址            proxy_read_timeout 604800s;#websocket心跳时间,默认是60s            proxy_http_version 1.1;            proxy_set_header Upgrade $http_upgrade;            proxy_set_header Connection "Upgrade";        }        location ~ .+.php {            fastcgi_pass  unix:/tmp/php-cgi.sock;            fastcgi_index index.php;            include fastcgi_params;            set $path_info "";            set $real_script_name $fastcgi_script_name;                if ($fastcgi_script_name ~ "^(.+?.php)(/.+)$") {                set $real_script_name $1;                set $path_info $2;            }            fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;            fastcgi_param SCRIPT_NAME $real_script_name;            fastcgi_param PATH_INFO $path_info;       }                       #防盗链开始        location ~ .*.(gif|jpg|jpeg|png|bmp|swf)${expires      30d;}location ~ .*.(js|css)?${expires      12h;}access_log  /home/wwwlogs/www1537ucn.log;}

三、安装swoole

编译安装:

wget http://pecl.php.net/get/swoole-1.9.3.tgz      //下载swoole    tar -zvxf swoole-1.9.3.tgz       //解压swoolecd swoole-1.9.3/;        //进入swoole/usr/local/php54/bin/phpize;        //生成configure./configure --with-php-config=/usr/local/php/bin/php-configmake && make install            //安装cd /phpstudy/server/php/lib/php/extensions/no-debug-non-zts-20121212    //查看是否安转上了swoole.so    (注意:此文件下边都是你安装的拓展)vim /phpstudy/server/php/etc/php.ini     //在php.ini添加extension=swoole.so加入到文件最后一行lnmp restart;   //重启nginx  php -m; //查看phpinfo,这时候swoole拓展已经装上了

 

四、服务器端运行程序

1、创建server.php放到项目的根目录即可

?php//实例化一个swoole的websocket服务监听本机的9501端口$server = new swoole_websocket_server("服务器IP", 9090);//只需要绑定要监听的ip和端口。如果ip指定为127.0.0.1,则表示客户端只能位于本机才能连接,其他计算机无法连接。//端口这里指定为9090,可以通过netstat查看下该端口是否被占用。如果该端口被占用,可更改为其他端口,如9502,9503等。$server-on('open', function (swoole_websocket_server $server, $request) {    echo "你好连接成功{$request-fd}";});$server-on('message', function (swoole_websocket_server $server, $frame) {    foreach($server-connections as $key = $fd) {        $user_message = $frame-data;        $server-push($fd, $user_message);    }});$server-on('close', function ($ser, $fd) {    echo "client {$fd} closed";});$server-start();?

2、由于swoole_server只能运行在CLI模式下,所以不要试图通过浏览器进行访问,这样是无效的,我们在命令行下面执行,注意一定要找到php的绝对路径php  server.php  (这行代码的意思是,把程序在服务器跑起来)

注意:php server.php命令运行后,下面的黑框关闭后将无法聊天。所以一般使用命令:nohup php server.php &

五、客户端

1、网页代码

!DOCTYPE htmlhtmlhead    meta charset="utf-8"    title聊天/title    style type="text/css"        #show{            width: 600px;            height: 300px;            overflow-y: scroll;        }        .my-message{            background-color: rgba(105, 105, 105, 0.64);            color: #9e0505;            width: 200px;            float: right;            padding: 10px;        }        .other-message{            background-color: rgba(105, 105, 105, 0.64);            color: #9e0505;            width: 200px;            float: left;            padding: 10px;        }    /style/headbody    div id="show"/div    div class="panel"        内容:textarea id="content"/textarea        收信人:input type="text" id="touser"        input type="button" id="send-btn" value="发送"        input type="button" id="close-btn" value="关闭"    /div/bodyscript src="__PUBLIC__/js/jquery-1.10.2.min.js" charset="utf-8"/scriptscript type="text/javascript"    var socket = new WebSocket("wss://域名");    $("#close-btn").click(function () {        socket.close();    })    $("#send-btn").click(function () {        var touser = $("#touser").val();        var content = $("#content").val();        var htmlstr = "divp class='my-message'我:"+content+"/p/div";        $("#show").append(htmlstr);        socket.send(content+"@"+touser);    })        socket.onmessage = function (p1) {        var htmlstr = "divp class='other-message'"+p1.data+"/p/div";        $("#show").append(htmlstr);    }/script/html

2、小程序端的代码

Uitls/websocket.js:

var url = 'wss://www.xxx.cn';//服务器地址function connect(user, func) {  wx.connectSocket({    url: url,    header: { 'content-type': 'application/json' },    success: function () {      console.log('websocket连接成功~')    },    fail: function () {      console.log('websocket连接失败~')    }  })  wx.onSocketOpen(function (res) {    wx.showToast({      title: 'websocket已开通~',      icon: "success",      duration: 2000    })    //接受服务器消息    wx.onSocketMessage(func);//func回调可以拿到服务器返回的数据  });  wx.onSocketError(function (res) {    wx.showToast({      title: 'websocket连接失败,请检查!',      icon: "none",      duration: 2000    })  })}//发送消息function send(msg) {  wx.sendSocketMessage({    data: msg  });}module.exports = {  connect: connect,  send: send}

JS:

// pages/socks/socks.jsconst app = getApp()var websocket = require('../../utils/websocket.js');var utils = require('../../utils/util.js');Page({  /**  * 页面的初始数据  */  data: {    newslist: [],    userInfo: {},    scrollTop: 0,    increase: false,//图片添加区域隐藏    aniStyle: true,//动画效果    message: "",    previewImgList: []  },  /**  * 生命周期函数--监听页面加载  */  onLoad: function () {    var that = this    if (app.globalData.userInfo) {      this.setData({        userInfo: app.globalData.userInfo      })    }    //调通接口    websocket.connect(this.data.userInfo, function (res) {      // console.log(JSON.parse(res.data))      var list = []      list = that.data.newslist      list.push(JSON.parse(res.data))      that.setData({        newslist: list      })    })  },  // 页面卸载  onUnload() {    wx.closeSocket();    wx.showToast({      title: '连接已断开~',      icon: "none",      duration: 2000    })  },  //事件处理函数  send: function () {    var flag = this    if (this.data.message.trim() == "") {      wx.showToast({        title: '消息不能为空哦~',        icon: "none",        duration: 2000      })    } else {      setTimeout(function () {        flag.setData({          increase: false        })      }, 500)      websocket.send('{ "content": "' + this.data.message + '", "date": "' + utils.formatTime(new Date()) + '","type":"text", "nickName": "' + this.data.userInfo.nickName + '", "avatarUrl": "' + this.data.userInfo.avatarUrl + '" }')      this.bottom()    }  },  //监听input值的改变  bindChange(res) {    this.setData({      message: res.detail.value    })  },  cleanInput() {    //button会自动清空,所以不能再次清空而是应该给他设置目前的input值    this.setData({      message: this.data.message    })  },  increase() {    this.setData({      increase: true,      aniStyle: true    })  },  //点击空白隐藏message下选框  outbtn() {    this.setData({      increase: false,      aniStyle: true    })  },  //发送图片  chooseImage() {    var that = this    wx.chooseImage({      count: 1, // 默认9      sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有      sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有      success: function (res) {        // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片        var tempFilePaths = res.tempFilePaths        // console.log(tempFilePaths)        wx.uploadFile({          url: 'wss://www.xxx.cn', //服务器地址          filePath: tempFilePaths[0],          name: 'file',          headers: {            'Content-Type': 'form-data'          },          success: function (res) {            if (res.data) {              that.setData({                increase: false              })              websocket.send('{"images":"' + res.data + '","date":"' + utils.formatTime(new Date()) + '","type":"image","nickName":"' + that.data.userInfo.nickName + '","avatarUrl":"' + that.data.userInfo.avatarUrl + '"}')              that.bottom()            }          }        })      }    })  },  //图片预览  previewImg(e) {    var that = this    //必须给对应的wxml的image标签设置data-set=“图片路径”,否则接收不到    var res = e.target.dataset.src    var list = this.data.previewImgList //页面的图片集合数组    //判断res在数组中是否存在,不存在则push到数组中, -1表示res不存在    if (list.indexOf(res) == -1) {      this.data.previewImgList.push(res)    }    wx.previewImage({      current: res, // 当前显示图片的http链接      urls: that.data.previewImgList // 需要预览的图片http链接列表    })  },  //聊天消息始终显示最底端  bottom: function () {    var query = wx.createSelectorQuery()    query.select('#flag').boundingClientRect()    query.selectViewport().scrollOffset()    query.exec(function (res) {      wx.pageScrollTo({        scrollTop: res[0].bottom // #the-id节点的下边界坐标      })      res[1].scrollTop // 显示区域的竖直滚动位置    })  },})

WXML:

!--pages/socks/socks.wxml--view class="news" bindtap='outbtn' view class="chat-notice" wx:if="{{userInfo}}"系统消息: 欢迎 {{ userInfo.nickName }} 加入聊天室/view view class="historycon" scroll-view scroll-y="true" class="history" scroll-top="{{scrollTop}}" block wx:for="{{newslist}}" wx:key     !-- 历史消息 -- !-- view class="chat-news"view style="text-align: left;padding-left: 20rpx;"image class='new_img' src="{{item.avatarUrl? item.avatarUrl:'images/avator.png'}}"/imagetext class="name"{{ item.nickName }}{{item.date}}/text/viewview class='you_left'block wx:if="{{item.type=='text'}}"view class='new_txt'{{item.content}}/view/blockblock wx:if="{{item.type=='image'}}"image class="selectImg" src="{{item.images}}"/image/block/view/view -- view{{item.date}}/view !--自己的消息 -- view class="chat-news" wx:if="{{item.nickName == userInfo.nickName}}" view style="text-align: right;padding-right: 20rpx;" text class="name"{{ item.nickName }}/text image class='new_img' src="{{userInfo.avatarUrl}}"/image /view view class='my_right' block wx:if="{{item.type=='text'}}" view class='new_txt'{{item.content}}/view /block block wx:if="{{item.type=='image'}}" image class="selectImg" src="{{item.images}}" data-src="{{item.images}}" lazy-load="true" bindtap="previewImg"/image /block /view /view !-- 别人的消息 -- view class="chat-news" wx:else view style="text-align: left;padding-left: 20rpx;" image class='new_img' src="{{item.avatarUrl? item.avatarUrl:'images/avator.png'}}"/image text class="name"{{ item.nickName }}/text /view view class='you_left' block wx:if="{{item.type=='text'}}" view class='new_txt'{{item.content}}/view /block block wx:if="{{item.type=='image'}}" image class="selectImg" src="{{item.images}}" data-src="{{item.images}}" lazy-load="true" bindtap="previewImg"/image /block /view /view /block /scroll-view /view /view view id="flag"/view !-- 聊天输入 -- view class="message" form bindreset="cleanInput" class="sendMessage" input type="text" placeholder="请输入聊天内容.." value="{{massage}}" bindinput='bindChange'/input view class="add" bindtap='increase'+/view button type="primary" bindtap='send' formType="reset" size="small" button-hover="blue"发送/button /form view class='increased {{aniStyle?"slideup":"slidedown"}}' wx:if="{{increase}}" view class="image" bindtap='chooseImage'相册 /view /view /view

WXSS:

/* pages/socks/socks.wxss */page { background-color: #f7f7f7; height: 100%; } /* 聊天内容 */ .news { padding-top: 30rpx; text-align: center; /* height:100%; */ box-sizing:border-box; } #flag{ margin-bottom: 100rpx; height: 30rpx; } .chat-notice{ text-align: center; font-size: 30rpx; padding: 10rpx 0; color: #666; } .historycon { height: 100%; width: 100%; /* flex-direction: column; */ display: flex; border-top: 0px; } /* 聊天 */ .chat-news{ width: 100%; overflow: hidden; } .chat-news .my_right { float: right; /* right: 40rpx; */ padding: 10rpx 10rpx; } .chat-news .name{ margin-right: 10rpx; } .chat-news .you_left { float: left; /* left: 5rpx; */ padding: 10rpx 10rpx; } .selectImg{ display: inline-block; width: 150rpx; height: 150rpx; margin-left: 50rpx; } .my_right .selectImg{ margin-right: 80rpx; } .new_img { width: 60rpx; height: 60rpx; border-radius: 50%; vertical-align: middle; margin-right: 10rpx; } .new_txt { max-width: 300rpx; display: inline-block; border-radius: 6rpx; line-height: 60rpx; background-color: #95d4ff; padding: 5rpx 20rpx; margin: 0 10rpx; margin-left: 50rpx; } .my_right .new_txt{ margin-right:60rpx; } .you{ background-color: lightgreen; } .my { border-color: transparent transparent transparent #95d4ff; } .you { border-color: transparent #95d4ff transparent transparent; } .hei{ margin-top: 50px; height: 20rpx; } .history { height: 100%; margin-top: 15px; padding: 10rpx; font-size: 14px; line-height: 40px; word-break: break-all; } ::-webkit-scrollbar { width: 0; height: 0; color: transparent; z-index: -1; }   /* 信息输入区域 */ .message{ position: fixed; bottom:0; width: 100%; } .sendMessage{ display: block; height: 80rpx; padding: 10rpx 10rpx; background-color: #fff; border-top: 2rpx solid #eee; border-bottom: 2rpx solid #eee; z-index:3; } .sendMessage input{ float:left; width: 66%; height: 100%; line-height: 80rpx; border-bottom: 1rpx solid #ccc; padding:0 10rpx; font-size: 35rpx; color: #666; } .sendMessage view{ display: inline-block; width: 80rpx; height: 80rpx; line-height: 80rpx; font-size: 60rpx; text-align: center; color: #999; border: 1rpx solid #ccc; border-radius: 50%; margin-left: 10rpx; } .sendMessage button { float: right; font-size: 35rpx; } .increased{ width:100%; /* height: 150rpx; */ padding: 40rpx 30rpx; background-color: #fff; } .increased .image{ width: 100rpx; height: 100rpx; border: 3rpx solid #ccc; line-height: 100rpx; text-align: center; border-radius: 8rpx; font-size:35rpx; } @keyframes slidedown { from { transform: translateY(0); } to { transform: translateY(100%); } } .slidedown { animation: slidedown 0.5s linear ; } .slideup { animation: slideup 0.5s linear ; } @keyframes slideup { from { transform: translateY(100%); } to { transform: translateY(0); } }

 

好了,代码就这些,到此已经可以实现实时聊天的效果了

最后


如何大家看了文章还有不懂或者其他问题,欢迎私信我或者评论

欢迎来CSDN关注我,喜欢作者支持一下

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

最新资讯

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎