【问题标题】:When to handle Socket.io notifications?何时处理 Socket.io 通知?
【发布时间】:2019-03-01 13:52:18
【问题描述】:

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

后端是PHP, MySQL(用于事件处理),+一点NodeJS, Socket.io(用于实时聊天和通知)


我已成功聊天:

当用户发送消息时,Socket.io 服务器会通过以下方式处理它:

  • 它将数据插入到数据库中
  • 如果成功则将消息发送给所有参与用户

/ 所以后端只是 Socket.io 服务器,它也处理数据库


工作正常。

但是有些事件并不意味着是实时的,但我仍然想使用 Socket.io 向给定用户发送通知

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

我已经编写了PHP文件用于在数据库中保存类似的东西,但是

通知部分应该怎么做,安全吗?


我想出了 3 个想法:

  1. 应用程序向我的 PHP+MySQL 后端发送一个 Web 请求,它在那里处理数据,然后在返回“成功”后,应用程序 (SWIFT) 向帖子所有者发送通知 (通过 S​​ocket.io XCode 吊舱)
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 文件发送任何内容,而是将整个“like”进程发送到我的 NodeJs、Socket.IO 服务器,它在那里处理它,将其保存到数据库中,然后发出通知(就像实时聊天部分一样,但这将是很多工作,因为我已经在 PHP 文件中编写了所有其他代码)

第一种情况对我来说是最理想的,但我害怕它会被破解..

因为如果我是第一种方式,后端NodeJs+Socket.io服务器不会检查点赞过程是否成功(因为它是在客户端检查的)

所以很可能任何人都可以发送虚假的“点赞”通知,比如十亿次。


那么也许第二个选项也不错,这样后端就可以同时处理检查和通知发送,但遗憾的是没有用于 PHP 的 Socket.io 插件

【问题讨论】:

  • 将 Socket.io 与 PHP 一起使用并不是处理进程间通信的最有效方式……(如果设置不正确,甚至会很危险)。我建议您将 Redis 视为 PHP 和 NodeJS 之间的消息代理。但是没有 PHP 的 NodeJS 仍然要强大得多,希望你有足够的动力从 PHP 继续前进。

标签: php node.js swift sockets notifications


【解决方案1】:

这样会更简单...

忘记 PHP,使用完整的 Nodejs:

Express(您也可以将它与 handlebarsi18n 结合使用以实现多语言目的)

使用 express,您可以为传入请求构建路由器 (GET,PUT,POST,...)

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

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(如果你想运行超过 1 个服务器实例,没问题,查找 socket.io-redis

您可以通过不同的方式将 express 与 socket.io 结合起来,您甚至可以为您的套接字协议使用基于 cookie 的身份验证。因此,当一个事件发生时,您实际上可以 100% 判断它是否是合法用户及其用户 ID。


为了防止喜欢的垃圾邮件...您必须以某种方式控制它们。 您应该存储 like 的操作,因此不能对同一个帖子重复多次(因此 user-id 和 post-id 似乎是这里的重要变量)



更新来了:

既然你很清楚你想要一个 php 和 nodejs 组合

Redis 是一种内存数据结构存储,可用作 数据库、缓存和消息代理。

PHPRedis @Github

Redis PubSub with PHP and Node.JS

A quick example of Node.js reading PHP session on Redis

使用 Redis,您可以轻松地从您的 nodejs 实例中侦听 php 事件。

我建议您也考虑一下您系统的未来扩展,并尝试学习更多 nodejs 以便能够从 php 继续前进。

【讨论】:

  • 非常感谢您提供如此详细的回答和建议!我很感激,并且得出了相同的结论,完全切换到 NodeJS 将是最好的选择,并且将来会为我节省很多时间:但是现在重写所有文件将是太多的工作+我更熟悉还使用 PHP:因此我尝试尽可能地结合。如果我有一个正在运行的 NodeJs->Socket.io 服务器和一个 PHP 服务器,你怎么看:可以从 PHP 发出通知吗?
  • @AndrasKarpati ,如果你必须 100% 使用 php,你必须选择一个不会成为未来瓶颈的选项......我将更新我的答案,提供更多细节
【解决方案2】:

您可以创建多个网络套接字通道。在您的情况下,您在 NodeJS 中使用 socket.io 添加了一个。您可以通过php方式添加另一个频道。

您可以像在 NodeJS 中收听一样收听该频道。

几个方便的链接 1.http://php.net/manual/en/book.sockets.php 2.How to create websockets server in PHP

【讨论】:

    【解决方案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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-03
      • 2013-02-03
      • 1970-01-01
      • 2013-01-10
      • 1970-01-01
      • 2019-12-04
      相关资源
      最近更新 更多