实时聊天、消息处理 - Socket.io、PHP、MySQL、Apache
Posted
技术标签:
【中文标题】实时聊天、消息处理 - Socket.io、PHP、MySQL、Apache【英文标题】:Real time chat, message handling - Socket.io, PHP, MySQL, Apache 【发布时间】:2018-01-03 01:40:24 【问题描述】:我是 Web 开发的初学者。最近我一直在开发一个完全基于 php 和 JS/jQuery 的实时聊天网站(我没有使用任何框架)。目前,我的设置只是简单的 AJAX 轮询,这显然不如我想要的那么好。我的数据库是mysql数据库。
我已经阅读了有关 websockets 的信息,我最初的新计划是使用 Socket.io 创建一个 NodeJS 服务器来处理消息 (How to integrate nodeJS + Socket.IO and PHP?),我考虑将这些消息存储在 MySQL 数据库中 (MySQL with Node.js)。
这是我目前所拥有的(不多,我想在我真正取得进展之前澄清如何取得进展)。这是我的测试设置,实际聊天中使用的 html 显然有点不同。
Node.js 服务器:
// NODE
var socket = require( 'socket.io' );
var express = require( 'express' );
var https = require( 'https' );
var http = require( 'http'); //Old
var fs = require( 'fs' );
var app = express();
//Working HTTPS server
var server = https.createServer(
key: fs.readFileSync('/etc/letsencrypt/live/%site%/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/%site%/fullchain.pem')
,app);
// var server = https.createServer( app ); Won't work cause no cert.
var io = socket.listen( server );
console.log("Server Started");
io.sockets.on( 'connection', function( client )
console.log( "New client !" );
client.on( 'message', function( data )
console.log( 'Message received ' + data); //Logs recieved data
io.sockets.emit( 'message', data); //Emits recieved data to client.
);
);
server.listen(8080, function()
console.log('Listening');
);
JS 客户端脚本:
var socket = io.connect('https://%site%:8080');
document.getElementById("sbmt").onclick = function ()
socket.emit('message', "My Name is: " + document.getElementById('nameInput').value + " i say: " + document.getElementById('messageInput').value);
;
socket.on( 'message', function( data )
alert(data);
);
我的超简单测试 HTML:
<form id="messageForm">
<input type="text" id="nameInput"></input>
<input type="text" id="messageInput"></input>
<button type="button" id="sbmt">Submits</button>
</form>
PHP 需要一些解释 - 当有人连接到我的网站时,我运行 session_start()
。这是因为我想要像匿名会话这样的东西。我通过$_SESSION
变量区分登录用户和匿名用户。匿名用户将 $_SESSION['anon']
设置为 true,也不会设置 $_SESSION['username']
。登录用户显然会将其反转。
谈到聊天 - 登录用户和匿名用户都可以使用它。当用户是匿名用户时,会从数据库或随机名称中生成一个随机用户名。当用户登录时,会选择他自己的用户名。现在我的 Ajax 轮询系统是这样工作的:
用户输入消息(在当前的聊天解决方案中,而不是我上面发送的测试 HTML)并按下回车键,然后对以下函数进行 AJAX 调用:
function sendMessage($msg, $col)
GLOBAL $db;
$un = "";
if (!isset($_SESSION['username']))
$un = self::generateRandomUsername();
else
$un = $_SESSION['username'];
try
$stmt = $db->prepare('INSERT INTO chat (id, username, timestamp, message, color) VALUES (null, :un, NOW(), :msg, :col)');
$stmt->bindParam(':un', $un, PDO::PARAM_STR);
$stmt->bindValue(':msg', strip_tags(stripslashes($msg)), PDO::PARAM_STR); //Stripslashes cuz it saved \\\ to the DB before quotes, strip_tags to prevent malicious scripts. TODO: Whitelist some tags.
$stmt->bindParam(':col', $col, PDO::PARAM_STR);
catch (Exception $e)
var_dump($e->getMessage());
$stmt->execute();
(请不要讨厌我糟糕的代码和糟糕的异常处理,这不是任何官方项目)。该函数将用户消息输入数据库。
为了接收新消息,我使用 JS 的setTimeout()
函数,在收到新消息后每隔 1 秒运行一次 AJAX 检查。我保存在 JS 中显示的最后一条消息的 ID,并将该 ID 作为参数发送给这个 PHP 函数(它每 1 秒运行一次):
/* Recieve new messages, ran every 1s by Ajax call */
function recieveMessage($msgid)
//msgid is latest msg id in this case
GLOBAL $db;
$stmt = $db->prepare('SELECT * FROM chat WHERE id > :id');
$stmt->bindParam(':id', $msgid, PDO::PARAM_INT);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return json_encode($result);
问题是:如何实现类似的东西,但使用我前面提到的 node.js 服务器和 websockets 设置?我需要以某种方式区分登录用户和匿名用户。我的第一个想法是从 node.js 服务器运行一个 ajax 调用到 PHP 并传递消息数据,PHP 会像现在一样将它插入到 DB 中。但是这种情况下的问题是如何再次将消息发送给客户端?在将消息输入数据库时应用用户名,这意味着我必须调用 AJAX 来保存到数据库,然后调用另一个 AJAX 来提取新输入的消息并将其发送给客户端,或者创建一个函数插入和提取并返回提取的消息。但是,当同时输入 2 条消息时,这不会引起问题吗?
是否可以在 Node.js 中访问 PHP 会话变量?然后我可以重写所有数据库查询以在 Node.js 服务器而不是 PHP 中工作。
如果我的代码或解释混乱,我再次道歉。
【问题讨论】:
请查看此开源插件,了解您所有与聊天相关的应用程序,如视频聊天、文本聊天等。webRTC @PandhiBhaumik 这确实是一个非常有趣的插件,我一定会研究它。不过,为了完成,我想继续我开始的方式,至少对于这个项目。由于我是初学者,我从探索所有可能性中获得了最多的经验:) 如果您有机会知道答案,请将其作为答案发布。我也很困在这里:(@Nae 【参考方案1】:所以,对于所有想知道并将在未来找到此主题的人:我没有找到我想要使用的解决方案的答案,但是我想出了其他东西,这里是一个描述:
我没有让 Node.js 服务器发送 AJAX 请求,而是像以前一样将来自客户端的 jQuery $.post() 请求留给 PHP 函数。
我接下来要做的是实现一个 MySQL 监听器,它检查 MySQL binlog 的变化。我使用了mysql-events
module。它使用所有数据检索新添加的行,然后使用 socket.io 发出函数将其发送到连接的客户端。我还不得不放弃 SSL,因为它显然讨厌我。这是一个小爱好项目,所以我真的不必为 SSL 操心那么多。
显然,最好的解决方案是在 Node.js 中对整个网络服务器进行编程,然后完全放弃 Apache。 Node.js 非常适合实时应用程序,它是一种非常容易学习和使用的语言。
我的 Node.js + Socket.io + mysql-events 设置:(忽略未使用的要求)
// NODE
var socket = require( 'socket.io' );
var express = require( 'express' );
var https = require( 'https' );
var http = require( 'http');
var fs = require( 'fs' );
var request = require( 'request' );
var qs = require( 'qs' );
var MySQLEvents = require('mysql-events');
var app = express();
/*Correct way of supplying certificates.
var server = https.createServer(
key: fs.readFileSync('/etc/letsencrypt/live/x/privkey.pem'),
cert: fs.readFileSync('/etc/letsencrypt/live/x/cert.pem'),
ca: fs.readFileSync('/etc/letsencrypt/live/x/chain.pem')
,app); */
var server = http.createServer( app ); // Won't work without cert.
var io = socket.listen( server );
console.log("Server Started");
//DB credentials
var dsn =
host: 'x',
user: 'x',
password: 'x',
;
var mysqlEventWatcher = MySQLEvents(dsn);
//Watcher magic, waits for mysql events.
var watcher = mysqlEventWatcher.add(
'newage_db.chat',
function (oldRow, newRow, event)
//row inserted
if (oldRow === null)
//insert code goes here
var res = JSON.stringify(newRow.fields); //Gets only the newly inserted row data
res.charset = 'utf-8'; //Not sure if needed but i had some charset trouble so i'm leaving this.
console.log("Row has updated " + res);
io.sockets.emit('message', "[" + res + "]"); //Emits to all clients. Square brackets because it's not a complete JSON array w/o them, and that's what i need.
//row deleted
if (newRow === null)
//delete code goes here
//row updated
if (oldRow !== null && newRow !== null)
//update code goes here
//detailed event information
//console.log(event)
);
io.sockets.on( 'connection', function( client )
console.log( "New client !" );
client.on( 'message', function( data )
//PHP Handles DB insertion with POST requests as it used to.
);
);
server.listen(8080, function()
console.log('Listening');
);
客户端 JavaScript 发送消息:
$('#txtArea').keypress(function (e)
if (e.which == 13 && ! e.shiftKey)
var emptyValue = $('#txtArea').val();
if (!emptyValue.replace(/\s/g, '').length) /*Do nothing, only spaces*/
else
$.post("/shana/?p=execPOST", $("#msgTextarea").serialize(), function(data)
);
$('#txtArea').val('');
e.preventDefault();
);
客户端 JavaScript 接收消息:
socket.on( 'message', function( data )
var obj = JSON.parse(data);
obj.forEach(function(ob)
//Execute appends
var timestamp = ob.timestamp.replace('T', ' ').replace('.000Z', '');
$('#messages').append("<div class='msgdiv'><span class='spn1'>"+ob.username+"</span><span class='spn2'style='float: right;'>"+timestamp+"</span><div class='txtmsg'>"+ob.message+"</div>");
$('#messages').append("<div class='dashed-line'>- - - - - - - - - - - - - - - - - - - - - - - - - - -</div>"); //ADD SCROLL TO BOTTOM
$("#messages").animate( scrollTop: $('#messages').prop("scrollHeight"), 1000);
);
);
不知何故,binlog 魔法破坏了时间戳字符串,所以为了清理它,我不得不替换一些字符串本身。
PHP 数据库插入函数:
function sendMessage($msg, $col)
GLOBAL $db;
$un = "";
if (!isset($_SESSION['username']))
$un = self::generateRandomUsername();
else
$un = $_SESSION['username'];
try
$stmt = $db->prepare('INSERT INTO chat (id, username, timestamp, message, color) VALUES (null, :un, NOW(), :msg, :col)');
$stmt->bindParam(':un', $un, PDO::PARAM_STR);
$stmt->bindValue(':msg', strip_tags(stripslashes($msg)), PDO::PARAM_LOB); //Stripslashes cuz it saved \\\ to the DB before quotes, strip_tags to prevent malicious scripts. TODO: Whitelist some tags.
$stmt->bindParam(':col', $col, PDO::PARAM_STR);
catch (Exception $e)
var_dump($e->getMessage());
$stmt->execute();
我希望这至少对某人有所帮助。随意使用此代码,因为无论如何我可能已经从互联网上复制了大部分代码:) 我会不时检查这个线程,所以如果您有任何问题,请发表评论。
【讨论】:
我有一个问题,拜托。我是 node js 的新手,我试图了解他的工作是什么......所以..在一个塑料 exprimation 中,node js 保持与 db 的持久连接,监视它的变化,但是当发现变化时,它如何注入新的信息进入页面?处理它的代码部分是什么? 我的意思是......MySQLEvents 将自行运行,无需触发器并且永远运行?像圣女一样? @BoteaFlorin 我已经很久没有参与这个项目了,所以我有点忘记了一些事情:)。 MySQLEvents 监视 MySQL 二进制日志 (dev.mysql.com/doc/refman/5.7/en/binary-log.html) 的更改,因此您需要启用它(iirc,从 5.6 开始默认启用)。它是如何监控它的,我不太确定。如果需要,您可以查看 MySQLEvents 的源代码。也看看这里:github.com/nevill/zongji 这是 MySQLEvents 的基础,也许你能找到一些答案。以上是关于实时聊天、消息处理 - Socket.io、PHP、MySQL、Apache的主要内容,如果未能解决你的问题,请参考以下文章
使用 mern 堆栈和 socket.io 的聊天应用程序在发送超过 20 条消息后变慢
需要在节点js,socket.io,express js,ionic 3的实时聊天应用程序中提供建议