WebSocket 聊天室,后端基于原生PHP和libevent,前端基于Vue 2.0
chat
├── docker Docker相关配置
│ ├── conf
│ │ ├── nginx nginx网站配置.conf
│ │ └── php PHP运行配置.ini
│ └── source 扩展包
├── html HTML页面
│ ├── dist 编译目录
│ ├── public
│ └── src
│ └── tests
├── php PHP服务脚本
│ ├── logs 日志目录
│ ├── server WsServer目录
│ └── upload 文件上传目录
└── ssl 证书目录
- 修改默认docker配置
docker/docker-compose.yml
中的目录映射 - 修改默认网站配置
docker/conf/nginx/www.conf
- 创建目录(保存Redis快照)
mkdir /home/chat/data
- 创建目录(保存上传文件)
mkdir /home/chat/php/upload
- 启动服务
docker-compose up
- 编译前端,浏览器访问 http://xxx/index.html
- 环境要求:Redis 2.6+、PHP 7.2+、libevent-dev、Apache/nginx
- 安装扩展
pecl install msgpack redis event
并添加到php.ini
中 - 启动服务
php Client.php -d -p 8080 -n 8
- 编译前端,浏览器访问 http://xxx/index.html
openssl x509 -inform DER -in certificate.cer -out certificate.crt
openssl x509 -inform PEM -in certificate.cer -out certificate.crt
- 创建目录
mkdir /home/chat/ssl
,并将证书放在该目录中 - 创建PHP项目配置
php/local.ini
,参考php/local.ini.example
(xxx替换为域名):
# 服务器带宽
bandwidth=100
# SSL证书配置
[ssl]
local_cert=/home/ssl/xxx.crt
local_pk=/home/ssl/xxx.key
ca_file=/home/ssl/xxx.ca.crt
# 安全验证,对应HTTP首部Host和Origin(服务器和客户端域名),多个用“,”隔开
[security]
server_name=xxx
client_name=yyy
- 修改nginx配置
docker/conf/nginx/www.conf
的server段,示例(xxx替换为域名):
listen 443 ssl;
server_name xxx;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
ssl_certificate /etc/nginx/ssl/xxx.crt;
ssl_certificate_key /etc/nginx/ssl/xxx.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
- 修改
docker-compose.yml
中的目录映射,示例:
nginx:
volumes:
- ../ssl:/etc/nginx/ssl
php:
volumes:
- ../ssl:/home/ssl
- 重启docker
docker-compose restart
- 浏览器访问 https://xxx/index.html
- -d 后台运行
- -p port 监听端口号
- -n num 开启的子进程数,至少为1
- 框架 Vue 2
- 主界面 lemon-imui
- 登录框 vue-js-modal
- 数据压缩 msgpackr
- JS编译器 Babel
- 提示框 vue-notification
- 图片预览 v-viewer@legacy
- 头像裁剪 vue-image-crop-upload
- 图标库 weui-icon
- 创建
.env.local
环境配置文件 - 设置环境变量,参考
.env.local.example
,示例(xxx替换为域名或IP):
# WebSocket服务器地址
VUE_APP_SERVER_URL=ws://xxx:8080 or wss://xxx:8080
# 文件服务器地址
VUE_APP_UPLOAD_URL=http://xxx or https://xxx
# SSL证书路径(https访问时必填,将证书文件放在某个文件夹下)
SSL_CERT=../ssl/xxx.crt
SSL_KEY=../ssl/xxx.key
# 公共资源路径(域名有二级路径时必填,比如 https://xxx.github.io/sub_path/)
PUBLIC_PATH=/ or /sub_path/
- 安装,运行(可能需要修改hosts)
npm install
# http访问
npm run serve
# https访问
npm run serve -- --port 443 --host xxx
- 创建
.env.production
环境配置文件 - 设置环境变量,参考
.env.production.example
- 编译
npm run build
- 将
dist
目录中的文件上传至服务器nginx相应目录下 - 浏览器访问 http://xxx/index.html
通过WebRTC协议实现
详见 Signaling and video calling
(Server: S, caller: A, callee: B C D)
A ->open media
-> request S to CREATE room[members] and ask all
B received -> accept
-> open media
-> create new RTCPeerConnection
-> setLocalDescription (triggers onicecandidate -> send RTCIceCandidate to A)
-> send offerB to A (through S)
-> request S to JOIN room
C received -> accept
-> open media
-> create new RTCPeerConnection
-> setLocalDescription (triggers onicecandidate -> send RTCIceCandidate to A)
-> send offerC to A (through S)
-> request S to JOIN room
D received -> deny
A -> received offerB
-> create new RTCPeerConnection
-> setRemoteDescription(offerB) and addIceCandidate
-> setLocalDescription (triggers onicecandidate -> send RTCIceCandidate to B)
-> send answerA to B
-> received offerC
-> create new RTCPeerConnection
-> setRemoteDescription(offerC) and addIceCandidate
-> setLocalDescription (triggers onicecandidate -> send RTCIceCandidate to C)
-> send answerA to C
B -> received answerA
-> setRemoteDescription(answerA) and addIceCandidate
-> (B-A connected)
C -> received answerA
-> setRemoteDescription(answerA) and addIceCandidate
-> (C-A connected)
S -> received B JOIN request
-> send to members(except B and A), but only A and B in room, so do nothing
-> received C JOIN request
-> send to members(except C and A), so send JOIN to B
B -> received C JOIN request
-> create new RTCPeerConnection
-> setLocalDescription (triggers onicecandidate -> send RTCIceCandidate to C)
-> send offerB to C (through S)
C -> received offerB
-> create new RTCPeerConnection
-> setRemoteDescription(offerB) and addIceCandidate
-> setLocalDescription (triggers onicecandidate -> send RTCIceCandidate to B)
-> send answerC to B
B -> received answerC
-> setRemoteDescription(answerC) and addIceCandidate
-> (B-C connected)
- 网站须以https和wss协议访问
- 公网需要TURN(Traversal Using Relays around NAT) Server中转多媒体数据
- 默认使用公网免费的TURN Server,详情访问 https://freestun.net/
- 若需要私有服务器,则:
- 搭建TURN Server,详情访问 https://github.com/coturn/coturn/
- 修改
webrtc.js
中的iceServers
配置 - 测试服务器是否可用 https://icetest.atec-systems.com/
注:TURN Server需要大量带宽
- Chrome
- Firefox
- Edge
- Opera
- ...
- 连接redis,以docker-redis为例,执行:
docker exec -it docker-redis-1 redis-cli
select 15
- 获取用户ID(xxx为用户名):
get "username:xxx"
- 获取用户信息(xxx为用户ID):
hgetall "user_id:xxx"
- 设置role_id(>0时为管理员):
hset "user_id:xxx" role_id 99
管理员权限目前包括:[大厅]移除用户
- 界面交互优化