何时处理Socket.io通知?

10

我正在开发一款用 SWIFT 编写的IOS社交应用。

后端使用的是PHP、MySQL(用于事件处理)和一些NodeJS、Socket.io(用于实时聊天和通知)。


我已经成功地创建了聊天功能:

当用户发送消息时,Socket.io服务器会按照以下方式进行处理:

  • 将数据插入到数据库中
  • 如果成功,则向所有参与者用户发出消息

/ 因此,后端仅为Socket.io服务器,它也处理数据库


效果很好。

但是,还有一些不需要实时处理的事件,但我仍然想通过Socket.io向指定用户发送通知。

例如:如果一个帖子被点赞,则向帖子所有者发送通知

我已经编写了用于将喜欢保存到数据库的PHP文件,但是

我该如何安全地处理通知部分?


我想到了3个方法:

  1. 应用程序向我的PHP + MySQL后端发送Web请求,然后在返回“成功”后,应用程序(SWIFT)通过Socket.io XCode pod向帖子所有者发送通知
func likePost(postId : Int, completion: @escaping (ActionResult?)->()){

        let connectUrl = URL(string: appSettings.url + "/src/main/like.php")
        var request = URLRequest(url: connectUrl!)
        request.httpMethod = "POST"
        let postString = "userId=\(userId)&session=\(session)&pId=\(postId)"
        request.httpBody = postString.data(using: String.Encoding.utf8)


        let task = URLSession.shared.dataTask(with: request) {
            (data: Data?, response: URLResponse?, error: Error?) in

            if error != nil {
                return completion(ActionResult(type: 0, code: 0, title: "error", message: "something went wrong"))
            }
            do {

                let responseJson = try JSONSerialization.jsonObject(with: data!, options: [])
                if let responseArray = responseJson as? [String: Any] {

                    let responseStatus = responseArray["status"] as? String
                    let responseTitle = responseArray["title"] as? String
                    let responseMessage = responseArray["message"] as? String


                    if responseStatus != "1" {
                        return completion(ActionResult(type: 0, code: 0, title: "error", message: "something went wrong"))
                    }

                    // SUCCESS, SEND NOTI WITH SOCKET.IO

                    socket.emit("notification_likedPost", ["postId": postId)

                    return completion(ActionResult(type: 1, title: "success", message: "yay"))

                }
            } catch {
                return completion(ActionResult(type: 0, code: 0, title: "error", message: "something went wrong"))
            }
        }
        task.resume()
    }
  1. 同样的,但在从PHP返回“成功”后,PHP文件本身处理Socket.IO通知的发出(我认为这是不可能的,我没有找到任何PHP-> Socket.io插件..)

-

  1. 该应用程序不会向我的Web PHP + MySQL文件发送任何内容,而是将整个“喜欢”过程发送到我的NodeJs,Socket.IO服务器中处理,将其保存到数据库中,然后发出通知(就像实时聊天部分一样,但这将需要很多工作,因为我已经编写了所有其他代码在PHP文件中)

第一种情况对我来说最理想,但我担心它容易被黑客攻击。

因为如果我使用第一种方式,后端的NodeJs + Socket.io服务器将不会检查“喜欢”过程是否成功(因为已在客户端检查)。

因此,任何人都可以发送虚假的“发布喜欢”通知,就像一亿次那样。


那么也许第二种选项也很棒,因为后端处理检查和通知发送,但可悲的是,PHP没有Socket.io插件


使用Socket.io与PHP处理进程间通信不是最有效的方式...(如果没有正确设置,甚至很危险)。我建议您查看Redis作为PHP和NodeJS之间的消息代理。但是,即使没有PHP,NodeJS仍然更加强大,希望您有足够的动力从PHP转向NodeJS。 - EMX
3个回答

7

如果使用Nodejs,会更加简单易行...

放弃PHP,转向Nodejs:

Express (你还可以结合 handlebarsi18n 实现多语言功能)

使用Express可以为传入的请求(GETPUTPOST等)构建路由器。

这意味着你可以使用它来呈现带有服务器端动态数据的页面。

const express = require('express');
const exphbs = require('express-handlebars');
const app = express();

// Register Handlebars view engine
app.engine('handlebars', exphbs());
// Use Handlebars view engine
app.set('view engine', 'handlebars');

var visit_counter = 0; 
app.get('/', (req, res) => {
  var time_stamp = Date.now(); visit_counter++
  res.render('index',{"timestamp":time_stamp,"visits":visit_counter});
});

app.listen(3000, () => {
  console.log('Example app is running → PORT 3000');
});

views/index.hbs 文件的样子如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Example App</title>
</head>
<body>

 <p> Current Time : {{timestamp}} </p>
 <p> Total Visits : {{visits}} </p>

</body>
</html>

这部分内容是将服务器端数据呈现在最终的HTML中的一个示例。
Socket.io(如果要运行多个服务器实例,没有问题,请查看socket.io-redis)。
你可以以不同的方式将express与socket.io结合起来,甚至可以为socket协议使用基于cookie的身份验证。因此,当一个事件到达时,您可以确定是否是合法用户和它的用户ID。
为了防止点赞垃圾邮件...你必须以某种方式控制它们。你应该存储喜欢的动作,这样它就不能在同一篇文章上重复多次(因此,用户ID和帖子ID似乎是重要的变量)。

下面是更新:
由于您明确表示想要一个php和nodejs组合:
Redis是一种内存数据结构存储器,可用作数据库、缓存和消息代理。
PHPRedis @Github
Redis PubSub with PHP and Node.JS
Node.js在Redis上读取PHP会话的快速示例
使用Redis,您可以轻松地从您的nodejs实例监听PHP事件。
我建议您还考虑系统未来的扩展,并尝试学习更多有关nodejs的知识,以便能够从php继续前进。

非常感谢您提供如此详细的答案和建议!我很感激,并且得出了相同的结论,完全转向NodeJS将是最好的选择,并且将来可以节省我很多时间:但是现在重写所有文件的工作量太大了+我更熟悉PHP:因此我尽可能地进行组合。如果我有一个运行中的NodeJs->Socket.io服务器和一个PHP服务器:从PHP发出通知是否可以? - Kárpáti András
@AndrasKarpati,如果你必须100%使用PHP,那么你必须选择一个不会成为未来瓶颈的选项...我会在我的答案中更新更多细节。 - EMX

3
我了解您的担忧,因为您的整个项目中PHP代码相对于其他框架/语言更加集中。为了解决您的问题,这里提供了适用于PHP v5.3及以上版本的Socket.io实现:https://github.com/walkor/phpsocket.io
通过使用它,您可以在您的PHP代码中使用socket.io库。下面是一个在PHP中使用Socket.io库的示例。
use Workerman\Worker;
use PHPSocketIO\SocketIO;

// listen port 2020 for socket.io client
$io = new SocketIO(2020);
$io->on('connection', function($socket){
    $socket->addedUser = false;
    // when the client emits 'new message', this listens and executes
    $socket->on('new message', function ($data)use($socket){
        // we tell the client to execute 'new message'
        $socket->broadcast->emit('new message', array(
            'username'=> $socket->username,
            'message'=> $data
        ));
    });
    // when the client emits 'add user', this listens and executes
    $socket->on('add user', function ($username) use($socket){
        global $usernames, $numUsers;
        // we store the username in the socket session for this client
        $socket->username = $username;
        // add the client's username to the global list
        $usernames[$username] = $username;
        ++$numUsers;
        $socket->addedUser = true;
        $socket->emit('login', array( 
            'numUsers' => $numUsers
        ));
        // echo globally (all clients) that a person has connected
        $socket->broadcast->emit('user joined', array(
            'username' => $socket->username,
            'numUsers' => $numUsers
        ));
    });
    // when the client emits 'typing', we broadcast it to others
    $socket->on('typing', function () use($socket) {
        $socket->broadcast->emit('typing', array(
            'username' => $socket->username
        ));
    });
    // when the client emits 'stop typing', we broadcast it to others
    $socket->on('stop typing', function () use($socket) {
        $socket->broadcast->emit('stop typing', array(
            'username' => $socket->username
        ));
    });
    // when the user disconnects.. perform this
    $socket->on('disconnect', function () use($socket) {
        global $usernames, $numUsers;
        // remove the username from global usernames list
        if($socket->addedUser) {
            unset($usernames[$socket->username]);
            --$numUsers;
           // echo globally that this client has left
           $socket->broadcast->emit('user left', array(
               'username' => $socket->username,
               'numUsers' => $numUsers
            ));
        }
   });
});

Worker::runAll();

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接