我是一个网络开发的初学者。最近,我一直在开发一个完全基于PHP和JS/jQuery的实时聊天网站(我没有使用任何框架)。目前,我的设置只是简单的AJAX轮询,这显然没有我希望的那么好。我的数据库是MYSQL数据库。
我读过关于WebSocket的书,我最初的新计划是用Socket创建一个NodeJS服务器。io将处理消息(如何集成nodeJS Socket.io和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);
});
我的超简单测试超文本标记语言:
<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\u start()
。这是因为我想要匿名会话之类的东西。我通过$\u SESSION
变量来区分登录用户和匿名用户。anon用户将$\u会话['anon']
设置为true,并且不会设置$\u会话['username']
。登录用户显然会将其反转。
说到聊天,登录用户和匿名用户都可以使用。当用户是匿名用户时,将从数据库或随机名称生成随机用户名。用户登录时,会选择自己的用户名。现在,我的Ajax轮询系统的工作原理如下:
用户输入消息(在当前聊天解决方案中,不是我上面发送的测试HTML),然后按enter键,并对以下函数进行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函数(它每1s运行一次):
/* 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服务器和WebSocket?我需要以某种方式区分登录用户和匿名用户。我的第一个想法是从node运行一个ajax调用。js服务器发送给PHP并传递消息数据,PHP会像现在一样将其插入数据库。但在这种情况下,问题是如何再次向客户发送消息?在将消息输入数据库时应用用户名,这意味着我必须调用AJAX将其保存到数据库中,然后调用另一个AJAX来提取新输入的消息并将其发送到客户端,或者创建一个函数来插入、提取和返回提取的消息。然而,当两条消息同时输入时,这不会导致问题吗?
是否有可能访问节点中的PHP会话变量。js?然后我可以重写所有数据库查询,以便在节点中工作。js服务器而不是PHP。
如果我的代码或解释混乱,我再次道歉。
所以,对于每个想知道并将在未来找到这个线程的人来说:我没有找到我想要使用的解决方案的答案,但是我想出了其他东西,这里有一个描述:
我没有让Node.js服务器发送AJAX请求,而是像以前一样,将来自客户端的jQuery$. post()请求发送给PHP函数。
接下来我做的是实现一个MySQL监听器,检查MySQL binlog中的更改。我使用了mysql事件
模块。它检索包含所有数据的新添加行,然后使用套接字。io emit函数将其发送到连接的客户端。我还不得不放弃SSL,因为它显然讨厌我。这是一个小爱好项目,所以我真的不必为SSL操心那么多。
最好的解决方案显然是在节点中对整个Web服务器进行编程。js,完全放弃Apache。节点。js对于实时应用程序来说非常棒,而且它是一种非常容易学习和使用的语言。
我的设置Node.jsSocket.iomysql-event:(忽略未使用的要求)
// 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 RECIEVE消息:
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 DB插入函数:
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、socket.io和express制作一个简单的聊天应用程序。但是,如果我单击main.jade文件中的send按钮,页面会刷新,并且不会出现任何消息。我在Firebug中也遇到这个错误: 加载页面时,与ws:/127.0.0.1:3000/socket.io/?eio=2&transport=websocket&sid=d_hnmpdxhed-j7lraaah的连接
问题内容: 我的聊天应用程序遇到问题,我需要能够向特定用户发送私人消息,我可以选择该特定用户,但由于某些原因无法弄清楚如何发送私人消息。 在下面,您将找到我服务器的代码,请提供帮助: 问题答案: 首先在聊天室中添加用户,以便在您的私人聊天室中轻松找到用户 您用于加入私人会议室的客户端代码 Join Room 您在客户端的JavaScript代码 } 您的服务器端代码以在您的房间中添加用户 });
我有一个由Express+MongoDB驱动的REST API服务器。有两个endpoint具有不同的资源。其中之一就是聊天API。我已经有了几个基本endpoint,比如: -创建chat -将消息发送到现有聊天 -获取指定聊天中的消息 我在想网络插座。例如,是否可以提供像这样的endpoint,它将代理套接字的服务器并在客户端连接到它? 有没有一些这样的API设计的好例子,我可以从中获得灵感,
问题内容: 我想为我的项目构建一个实时聊天系统,但实际上我在使用Redis时遇到了一些问题,因为我希望尽可能地更好地存储数据。 我的问题: 我想使用Socket Io在一个封闭的小组(两个人)中进行实时聊天,但是如何存储消息呢? Redis是一个键值存储,这意味着如果我要存储某些内容,则需要在存储之前向数据添加唯一键。 如果同一用户发布多个消息,那么我将在redis中使用哪些键?我正在考虑将唯一I
我正在用插座找一个指定的房间。io,但其给出的错误为“未定义房间”。下面是我的代码。谁能帮忙吗?或者解释什么问题 });