workermen (二)
(一)实现注册
user表
字段名称 | 类型 | 注释 |
---|---|---|
id | int | 自增id |
username | varchar | 名字 |
password | varchar | 密码 |
注册
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>注册</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div style="width:600px;height:500px;margin: 30px auto;border:1px solid rgb(51, 23, 13);">
<h1 style="text-align: center">注册</h1>
<form action="register.php" method="POST">
<div class="form-group">
<label for="exampleInputEmail1">用户名</label>
<input type="username" name="username" class="form-control" id="exampleInputEmail1" placeholder="username">
</div>
<div class="form-group">
<label for="exampleInputPassword1">密码</label>
<input type="password" name="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<button type="submit" class="btn btn-primary" style="margin: 30px 270px;">注册</button>
</form>
</div>
</body>
</html>
register.php
$pdo = new \PDO('mysql:host=127.0.0.1;dbname=chat', '用户名', '密码');
$pdo->exec('SET NAMES utf8'); //编码格式
//接收值
$username = $_POST['username'];
$password = md5($_POST['password']);
//插入
$sql = "insert into user(username,password) values('$username','$password')";
$c = $pdo->exec($sql);
// 判断是否成功
if($c){
echo "<script>alert('您已注册成功,返回登录');location='index.html'</script>";
exit;
}
演示:
(二)、登录及首页
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>聊天室</title>
<link rel="stylesheet" type="text/css" href="css/chat.css" />
<style>
span {
color: #999;
margin-left: 10px;
}
</style>
</head>
<body class="keBody">
<div id="app">
<div class="kePublic">
<!--效果html开始-->
<div class="content">
<div class="chatBox">
<div class="chatLeft">
<div class="chat01">
<div class="chat01_title">
<ul class="talkTo">
<li>
<a href="javascript:;">{{ messageList.username }}</a>
<a @click="logout" href="javascript:;">退出</a>
</li>
</ul>
<a class="close_btn" href="javascript:;"></a>
</div>
<div class="chat01_content" style="padding: 10px;" id="chat01_content">
<dl v-for="(v,k) in messageList" :key="k">
<dt><strong>{{v.username}}</strong> 在 <time>{{ v.datetime }}</time> 说:</dt>
<dd>{{ v.content }}</dd>
</dl>
</div>
</div>
<div class="chat02">
<div class="chat02_title">
<label class="chat02_title_t">
<a target="_blank">聊天记录</a>
</label>
<div class="wl_faces_box">
<div class="wl_faces_content">
<div class="title">
<ul>
<li class="title_name">常用表情</li>
<li class="wl_faces_close">
<span> </span>
</li>
</ul>
</div>
<div class="wl_faces_main">
<ul>
<li>
<a href="javascript:;">
<img src="img/emo_01.gif" />
</a>
</li>
<li>
<a href="javascript:;">
<img src="img/emo_02.gif" />
</a>
</li>
<li>
<a href="javascript:;">
<img src="img/emo_03.gif" />
</a>
</li>
</ul>
</div>
</div>
<div class="wlf_icon">
</div>
</div>
</div>
<div class="chat02_content">
<textarea id="textarea" v-model="content" placeholder="请输入内容按enter键快速发消息"></textarea>
</div>
<div class="chat02_bar">
<ul>
<li style="left: 20px; top: 10px; padding-left: 30px;">
apppws创建</li>
<li style="right: 5px; top: 5px;">
<img @click="send" src="img/send_btn.jpg" id="send_btn">
</li>
</ul>
</div>
</div>
</div>
<div class="chatRight">
<div class="chat03">
<div class="chat03_title">
<label class="chat03_title_t">成员列表</label>
</div>
<div class="chat03_content">
<!-- <ul>
<li class="choosed" v-for="(v,k) in userList">
<label class="offline">
</label>
<a href="javascript:;">
<img src="img/head/2015.jpg">
</a>
<a href="javascript:;" class="chat03_name">{{ v.username }}</a>
</li>
</ul> -->
<select v-model="to">
<option value="">发给所有用户</option>
<option :value="k" v-for="(v,k) in userList">{{v}}</option>
</select>
</div>
</div>
</div>
<div style="clear: both;">
</div>
</div>
</div>
<!--效果html结束-->
<!-- 登录表单 -->
<div :class="{'login':true,'hide':layerHide}">
<h1>登录</h1>
<div><input v-model="username" type="text" placeholder="输入用户名"></div>
<div><input v-model="password" type="password" placeholder="输入密码"></div>
<div><input @click="dologin" type="button" value="登录"></div>
</div>
<!-- 登录时黑色背景层 -->
<div :class="{'layer':true,'hide':layerHide}"></div>
</div>
</div>
</body>
</html>
<script src="./axios.min.js"></script>
<script src="./vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
messageList: [], //消息列表
userList: [], // 在线用户列表
message: '', // 消息框中的内容
ws: null, // websocket 对象
layerHide: false, // 是否隐藏登录框
host: '127.0.0.1:8484', // 服务器地址
username: '', // 当前用户名
password: '',
to: '',
content: ''
},
methods: {
// 登录
dologin: function () {
axios.post('http://localhost:9999/login.php', {
username: this.username,
password: this.password
}).then(function (res) {
console.log(res.data);
if (res.data.code == 200) {
localStorage.setItem('jwt_token', res.data.jwt)
this.layerHide = true; // 隐藏登陆框
location.href = "index.html"
}
else {
alert(res.data.error)
}
})
},
// 退出
logout: function () {
// 断开连接
this.ws.close()
localStorage.removeItem('jwt_token')
location.href = "index.html"
},
// 发送消息
send: function () {
// 判断消息是发给谁的
if (this.to == '') {
// 群发:
this.ws.send('all:' + this.content);
} else {
// 发给某个人
this.ws.send(this.to + ':' + this.content);
}
this.content = ''
},
open: function () {
alert('连接成功');
},
// 收到消息时调用
ws_message: function (e) {
// 解析JSON
let data = JSON.parse(e.data)
// 根据消息的类型发处理消息
if (data.type == 'users'){
this.userList = data.users
}
else{
this.message=data.ws_message
}
this.messageList.push(data)
},
},
// 页面初始化
created: function () {
var token = localStorage.getItem('jwt_token')
if (token == undefined)
alert('请登录');
else {
this.layerHide = true; // 隐藏登陆框
// 连接聊天服务器
this.ws = new WebSocket('ws://127.0.0.1:8484?token=' + token)
this.ws.onopen = this.open
this.ws.onmessage = this.ws_message
this.ws.onerror = this.error
}
},
})
</script>
显示:
登录服务的代码:
login.php
<?php
// 引入自动加载的 扩展包
require('./vendor/autoload.php');
// 生成令牌
use \Firebase\JWT\JWT;
// 连接数据库
$pdo = new \PDO('mysql:host=127.0.0.1;dbname=chat', 'root', '');
$pdo->exec('SET NAMES utf8');
// 接收原始数据
$post = file_get_contents('php://input');
// 把JSON转成数组
$_POST = json_decode($post, TRUE);
// 查询数据库
$stmt = $pdo->prepare("select * from user where username=? and password=?");
$stmt->execute([
$_POST['username'],
md5($_POST['password'])
]);
// 取出来所有信息
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// var_dump($user);exit;
if($user)
{
$key = 'abcd1234';
$now = time();
$data = [
'id' => $user['id'],
'name' => $user['username'],
];
// var_dump($data);exit;
// 为这个数据生成令牌
$jwt = JWT::encode($data, $key);
// 返回 JSON 数据
echo json_encode([
'code'=>'200',
'jwt'=>$jwt,
]);
}
else
{
// 返回 JSON 数据
echo json_encode([
'code'=>'403',
'error'=>'用户名或者密码错误!',
]);
}
*里面包含lwt令牌
下载:composer require firebase/php-jwt
*vue.js
的下载
查看及安装:https://cn.vuejs.org/
*axios.js
的使用
安装及下载:https://www.kancloud.cn/yunye/axios/234845
(三)、实现服务器
start.php
<?php
use Workerman\Worker;
use Firebase\JWT\JWT;
// 引入workerman 自动加载
require_once __DIR__ . '/Workerman-master/Autoloader.php';
require('./vendor/autoload.php');
//保存所有用户
$allUsers = [];
// 客户端用户数据
$userConn = [];
// 有连接时调用
function connect($connection)
{
$connection->onWebSocketConnect = function ($connection, $http_header) {
global $allUsers, $userConn, $worker;
// 解析jwt令牌
try {
$key = 'abcd1234';
$data = JWT::decode($_GET['token'], $key, array('HS256'));
// 把id和name保存到这个连接上
$connection->uid = $data->id;
$connection->uname = $data->name;
// 保存当前连接到所有用户的数组中
$allUsers[$data->id] = $data->name;
$userConn[$data->id] = $connection;
// 如果用户连接成功 就通知所有其他客户端有新的客户端连接
foreach ($worker->connections as $v) {
$v->send(json_encode([
'username' => $data->name,
'content' => '加入了聊天室',
'datetime' => date('Y-m-d H:i'),
'type' => 'users',
'users' => $allUsers
]));
}
} catch (\Firebase\JWT\ExpiredException $e) {
// 关闭连接
$connection->close();
} catch (\Exception $e) {
// 关闭连接
$connection->close();
}
};
}
// 当收到数据时调用
function message($connection, $data)
{
// var_dump($data);
global $worker, $allUsers;
// 从消息中解析出第一个 :前面的内容 判断是单发还是群发
// 将字符串转为数组
$ret = explode(':', $data);
// 取出第一个元素 并去掉:前面的内容
$code = $ret[0];
unset($ret[0]);
// 再把数组拼成字符串
$rawData = implode(':', $ret);
// 判断第一个元素
if ($code == "all") {
// 群发
foreach ($worker->connections as $v) {
$v->send(json_encode([
'username' => $connection->uname,
'content' => $rawData,
'datetime' => date('Y-m-d H:i'),
'type' => 'users',
'users' => $allUsers
]));
}
} else {
// 引进保存客户端与对应用户id的数组
global $userConn;
$userConn[$code]->send(json_encode([
'username' => $connection->uname,
'datetime' => date('Y-m-d H:i'),
'type' => 'message',
'content' => $rawData,
'message' => $rawData
]));
}
}
// 当有客户端断开连接时调用
function close($connection, $data)
{
global $allUsers, $worker;
// 从用户列表数组中删除当前退出的用户
unset($allUsers[$connection->id]);
// 给所有用户发消息
foreach ($worker->connections as $v) {
$v->send(json_encode([
'username' => $data->name,
'content' => '退出了聊天室',
'datetime' => date('Y-m-d H:i'),
'type' => 'users',
'users' => $allUsers
]));
}
}
//创建一个workerman 的监听端口号
$worker = new Worker('websocket://0.0.0.0:8484');
//进程
$worker->count = 1;
// 设置回调函数
$worker->onConnect = 'connect';
$worker->onMessage = 'message';
$worker->onClose = 'close';
// 运行
$worker->runAll();
*启动:php start.php start
最终结果: 可以互相通信
源码:https://github.com/apppws/chatroom
*温馨提示:有什么问题或者有好的建议请发评论。