广告---:工作室
浓浓年味小年夜,还在公司上班嘞,不过刚刚歇下,就把最近在看swoole写的聊天室奉上来,我这个人,对通信方面的学习与实践,一直是乐此不疲的。
基本功能:
注册、登录、在线列表刷新、 群发、指定对象发
说明:我都是放在一个聊天控件里,没有把群聊和单聊分开,因为没那么多时间去做整合了。目前在考虑多去了解底层实现TCP、UDP服务器。
写的不好,多多见谅!
WebSocket服务器:
<?php
class WebSocket
{
private $message = null;
private $ws = null;
private $usr_array = array();
private $user_hash = "user_hash";
private $conn_hash = "conn_hash";
private $redis_conn = null;
public function __construct()
{
//连接redis服务器
$this->redis_conn = new Redis();
$this->redis_conn->connect('127.0.0.1', 6379);
//创建websocket服务器对象,监听0.0.0.0:8000端口
$this->ws = new swoole_websocket_server("0.0.0.0", 8000);
//监听WebSocket连接打开事件
$this->ws->on('open', function ($ws, $request) {
});
//监听WebSocket消息事件
$this->ws->on('message', function ($ws, $obj) {
echo "Message: {$obj->data}\n";
$data_json = json_decode($obj->data, true);
$this->message = $data_json['data'];
switch ($data_json['code']) {
case 1:
//s
$this->login($obj->fd);
break;
case 2:
$this->sendMsg();
break;
case 3:
$this->register($obj->fd);
break;
default:
break;
}
});
//监听WebSocket连接关闭事件
$this->ws->on('close', function ($ws, $fd) {
echo "client-{$fd} is closed\n";
//刷新好友列表
$this->redis_conn->hDel($this->conn_hash, $fd);
$this->send(true, 0, $this->echoMsg(4, '', $this->all_in_line()));
});
$this->ws->start();
}
//用户注册
private function register($id)
{
# phone,username,password 检测注册信息不得为空
if (!$this->checkArray($this->message[0])) {
$this->send(false, $id, $this->echoMsg(1000, 'have null data!', ''));
return;
}
$user_phone = $this->message[0]['phone'];
$user_array = array(
"password" => $this->message[0]['password'],
"username" => $this->message[0]['username']
);
if (!empty($this->getHash($this->user_hash, $user_phone))) {
$this->send(false, $id, $this->echoMsg(1000, 'this phone is register!', ''));
} else {
$this->setHash($this->user_hash, $user_phone, serialize($user_array));
$this->send(false, $id, $this->echoMsg(3, '', ''));
}
}
private function login($id)
{
$psd = $this->message[0]['psd'];
$usr = $this->message[0]['usr'];
$in_res = $this->getHash($this->user_hash, $usr);
if (empty($in_res)) {
$this->send(false, $id, $this->echoMsg(1000, 'user not exist,please register!', $this->message));
} else {
$in_res = unserialize($in_res);
if ($in_res['password'] == $psd) {
//save user info
$this->setHash($this->conn_hash, $id, $in_res['username']);
$this->send(false, $id, $this->echoMsg(1, array("id" => $id, "username" => $in_res['username']), ''));
//change friends item
$this->send(true, 0, $this->echoMsg(4, '', $this->all_in_line()));
} else {
$this->send(false, $id, $this->echoMsg(1000, 'password is error!', ''));
}
}
}
//search all in line
private function all_in_line()
{
$res = $this->redis_conn->hGetAll($this->conn_hash);
return array($res);
}
// send msg
private function send($is_all = false, $id = 0, $msg = '')
{
if ($is_all) {
foreach ($this->ws->connections as $fd) {
$this->ws->push($fd, $msg);
}
} else {
$this->ws->push($id, $msg);
}
}
//发送消息,并根据发送对象ID,如果为0则是发送给所有人
private function sendMsg()
{
$send_id = intval($this->message[0]['to']);
if ($send_id != 0) {
//person
$this->send(false, $send_id, $this->echoMsg(2, '', $this->message[0]));
} else {
//all
$this->send(true, 0, $this->echoMsg(2, '', $this->message[0]));
}
}
//返回json字符串
private function echoMsg($code, $msg, $data)
{
$redata = array();
//$data不为空
if (!empty($data)) {
if (is_array($data)) {//传入数组
$redata = $data;
} else {//传入字符串
$redata = $data;
}
}
if ($code == 0) {//识别码为0
$reMsg = array('code' => $code, 'msg' => $msg);
return json_encode($reMsg);
} else {//识别码不为0
$reMsg = array(
'code' => $code,
'msg' => $msg,
'data' => $redata
);
return json_encode($reMsg);
}
}
//save login state
public function setHash($data = '', $key = '', $value = '')
{
# hash
$in_login = $this->redis_conn->hGet($data, $key);
if (empty($in_login)) {
$this->redis_conn->hSet($data, $key, $value);
return true;
} else {
return false;
}
}
public function getHash($data = '', $key = '')
{
return $this->redis_conn->hGet($data, $key);
}
public function checkArray($data_array)
{
foreach ($data_array as $key => $value) {
if (empty($value)) {
return false;
}
}
return true;
}
}
new WebSocket();
然后是前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello , ChatRoom</title>
<style>
.a {
margin-left: 10px;
}
.error {
border: solid #ee0000 1px;
border-radius: 3px;
padding: 10px;
}
.chat_div {
display: flex;
flex-direction: row;
height: 500px;
min-width: 500px;
}
ul {
list-style: none;
margin: 10px;
}
li {
}
table {
width: 95%;
margin: 10px;
}
.chat_left {
width: 30%;
border: solid #ccc 1px;
}
.chat_right {
width: 70%;
border: solid #ccc 1px;
overflow: hidden;
}
.chat_right_header {
text-align: center;
border-bottom: solid #ccc 1px;
padding: 5px;
}
.chat_right_table {
height: 60%;
overflow: auto;
}
.chat_right_bottom {
border-top: solid #ccc 1px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}
.td_left {
float: left;
}
.td_right {
float: right;
}
textarea {
width: 98%;
height: 100px;
margin: 10px;
}
td {
padding: 10px;
}
.span_left {
border: solid #66ccff 2px;
background: #66ccff;
border-radius: 3px;
padding: 5px;
color: #fff;
}
.span_right {
border: solid #ee0000 2px;
background: #ee0000;
border-radius: 3px;
padding: 5px;
color: #fff;
}
.btn_send {
float: right;
margin: 0 0 0 0;
width: 98%;
}
.send_obj {
color: #ee0000;
}
</style>
</head>
<body>
<div class="error" style="display: none;">
<span>error : not connect server ! now is restart ...<label style="color: #ee0000;margin-left: 10px;"
class="error_tips"></label></span>
<p>
<button οnclick="switch_tab(2)">close</button>
</p>
</div>
<!--register page-->
<div class="register" style="display: none;">
<h3>please register a count!</h3>
<p><input type="text" placeholder="your phone" id="register_phone"></p>
<p><input type="text" placeholder="your username" id="register_username"></p>
<p><input type="password" placeholder="your password" id="register_password"></p>
<p>
<button οnclick="register()">register</button>
<label class="a">have count?<a href="javascript:void(0)" class="a" οnclick="switch_tab(2)">go
login</a></label></p>
</div>
<!--login page-->
<div class="login">
<h3>please login!</h3>
<p><input type="text" placeholder="phone" id="login_usernum"></p>
<p><input type="password" placeholder="password" id="login_password"></p>
<p>
<button οnclick="login()">login</button>
<label class="a">no count? <a href="javascript:void(0)" class="a" οnclick="switch_tab(1)">go
register</a></label></p>
</div>
<div>
<p id="res"></p>
</div>
<div>
</div>
<div>
<div class="chat_header">
</div>
<div class="chat_div">
<div class="chat_left">
<ul class="friend_item">
</ul>
</div>
<div class="chat_right">
<div class="chat_right_header">
Send To <span class="send_obj">all</span>
</div>
<div class="chat_right_table">
<table>
<tbody>
</tbody>
</table>
</div>
<div class="chat_right_bottom">
<textarea placeholder="input" id="b"></textarea>
<button class="btn_send" οnclick="sendMsg()">Send</button>
</div>
</div>
</div>
</div>
<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
var is_conn = false;
var websocket = null;
var is_login = false;
var usr = '';
var i = 0;
var re_time = null;
var send_id = 0;
var send_name = 'all';
var me_id = 0;
function init() {
var wsServer = 'ws://192.168.146.132:8000';
websocket = new WebSocket(wsServer);
websocket.onopen = function (evt) {
is_conn = true;
console.log("Connected to WebSocket server.");
if (re_time !== null) {
clearInterval(re_time);
}
switch_tab(2);
};
websocket.onclose = function (evt) {
console.log("Disconnected");
switch_tab(4);
is_conn = false;
if (re_time === null) {
re_time = setInterval(re_conn, 1000);
}
};
websocket.onmessage = function (evt) {
console.log('Retrieved data from server: ' + evt.data);
var json_data = JSON.parse(evt.data);
switch (json_data.code) {
case 1:
// login success
is_login = true;
switch_tab(3);
usr = json_data.msg['username'];
me_id = json_data.msg['id'];
$(".chat_header").html("<h3 style='text-align: center;'>Welcome : " + usr + "</h3>");
console.log("This id is :" + me_id);
break;
case 2:
// get message
if (json_data.data.from !== me_id) {
create_chat(true, json_data.data.from_name+"@"+json_data.data.to_name+" : ",json_data.data.msg);
}
break;
case 3:
// register success
alert("register success!");
$("#register_phone").val("");
$("#register_username").val("");
$("#register_password").val("");
break;
case 4:
//get all in line
create_friend_item(json_data.data);
break;
case 1000:
$("#res").html("<p style='color: #ee0000;'>" + json_data.msg + "</p>");
break;
}
};
websocket.onerror = function (evt, e) {
console.log('Error occured: ' + evt.data);
};
}
function register() {
var register_json = {
'code': 3,
'msg': '',
'data': [{
'phone': $("#register_phone").val(),
'username': $("#register_username").val(),
'password': $("#register_password").val()
}]
};
var json = JSON.stringify(register_json);
websocket.send(json);
}
function login() {
if (is_login) {
alert("this window is login!");
} else {
var login_json = login_arr = {
'code': 1,
'msg': '',
'data': [{
'psd': $("#login_password").val(),
'usr': $("#login_usernum").val()
}]
};
var json = JSON.stringify(login_json);
websocket.send(json);
}
}
function sendMsg() {
var send_msg = $("#b").val();
if (send_msg) {
var login_json = {
'code': 2,
'msg': '',
'data': [{
'from': me_id,
'from_name':usr,
'to': send_id,
'to_name':send_name,
'msg': send_msg
}]
};
var json = JSON.stringify(login_json);
websocket.send(json);
create_chat(false, "","@"+send_name+" "+send_msg);
$("#b").val("");
} else {
alert("please input something");
}
}
function create_friend_item(data) {
$(".friend_item li").remove();
$(".friend_item").append("<li><a href='javascript:void(0)' id='0'>all</a></li>");
for (var i = 0; i < data.length; i++) {
for (var key in data[i]) {
if (parseInt(key) !== parseInt(me_id)) {
$(".friend_item").append("<li><a href='javascript:void(0)' id='" + key + "'>" + data[i][key] + "</a></li>");
}
}
}
$(".friend_item li a").click(function () {
$(".send_obj").text($(this).text());
send_id = parseInt($(this).attr("id"));
send_name = $(this).text();
});
}
//聊天后创建聊天消息列表
function create_chat(left, title,msg) {
if (left) {
$("tbody").append("<tr><td class='td_left'>"+title+"<span class='span_left'>" + msg + "</span></td>/tr>")
} else {
$("tbody").append("<tr><td class='td_right'>"+title+"<span class='span_right'>" + msg + "</span></td>/tr>")
}
var root = document.getElementsByClassName("chat_right_table");
//root.scrollIntoView(true);
}
//展示哪一个面板
function switch_tab(res) {
all_none();
switch (res) {
case 1:
$(".register").css("display", 'block');
break;
case 2:
$(".login ").css("display", 'block');
break;
case 3:
$(".chat_div").css("display", 'flex');
break;
case 4:
$(".error").css("display", 'block');
break;
}
}
//隐藏所以面板
function all_none() {
$(".register").css("display", 'none');
$(".login").css("display", 'none');
$(".chat_div").css("display", 'none');
$(".error").css("display", 'none');
}
function re_conn() {
i++;
$(".error_tips").text(i);
init();
}
//捕获文档对象的按键弹起事件
$(document).keyup(function (e) {
//按键信息对象以参数的形式传递进来了
if (e.keyCode === 13) {
//回车后
sendMsg();
}
});
init();
</script>
</body>
</html>
这样的话一个WebScoket聊天服务器和客户端就出来了,后续更改前端部分也就可以做成弹幕、互动等效果。swoole整块我还没看完,就先做了这个出来,后续看完还会再写其他内容。
很抱歉的是因为我在Ubuntu下写的代码,没有装中文输入法(有些部分是我写文章的时候添加注释),英语水平有点渣,影响阅读、敬请谅解!
广告---:工作室