Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to handle Socket.io notifications?

I am developing an IOS social application that is written in SWIFT.

The backend is PHP, MySQL (for event handling), + a bit of NodeJS, Socket.io (for realtime chat and notifications)


I have made the chat successfully:

When the user sends a message the Socket.io server handles it the following way:

  • it inserts the datas to the database
  • if successful then emits the message to all the participant users

/ so for this the backend is only the Socket.io server, which handles the database aswell


Works fine.

But then there are events that are not meant to be real time, but still I want to send a notification to the given user with Socket.io

for example: if a post has been liked, then send a noti to the posts owner

I have already written the PHP files for saving the like in the database, but

How should I do the notification part, safe?


I have came up with 3 ideas:

  1. The app sends a web request to my PHP+MySQL backend, it handles the data there, then after returning back "success", the application (SWIFT) sends a notification to the post owner (via 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. same, but after returning back "success" from the PHP, itself (the PHP file) handles the Socket.IO notification emitting as well (I think this is not possible, I haven't found any PHP->Socket.io plugins..)

-

  1. The app does not send anything to my web PHP+MySQL file, instead it sends the whole "like" process to my NodeJs, Socket.IO server, it handles it there, saves it to the database, then emits the notifications (Just like the real time chat part, but this would be a lot work because I have already written all the other code in PHP files)

The first case is the most ideal for me, but I am scared that it would be hackable..

Because if I do it the first way, the backend NodeJs+Socket.io server won't check if the liking process was successful (because it was checked client-sided)

so it is likely that anyone could send fake "post like" notifications, like a billion times.


Then maybe the second option would be great as well, so that back-end handles both checking, and notification sending, but sadly there's no Socket.io plugin for PHP

like image 832
Kárpáti András Avatar asked Sep 25 '18 21:09

Kárpáti András


3 Answers

It would be much more simpler to ...

Forget PHP, Go full Nodejs:

Express (you can also combine it with handlebars & i18n for multi-language purpose)

With express you can build a router for incoming requests (GET,PUT,POST,...)

This means that you can use it to render pages with server-side dynamic data

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');
});

The views/index.hbs file would look like this :

<!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>

This above part is an example of server-side data being rendered in the final html.


Socket.io (if you want more than 1 instance of the server running, no problem, lookup socket.io-redis)

You can combine express with socket.io in different ways, you could even use cookie-based authentication for your socket protocol. so when an event is coming in you could actually tell 100% if its a legit user and its user-id.


To prevent the spam of likes... you have to control them somehow. You should store the action of the like, so it cant be repeated more than once for the same post (so user-id & post-id seem to be the important variables here)



Here comes the update :

Since you made quite clear that you want a php & nodejs combo :

Redis is an in-memory data structure store which can be used as a database, a cache and a message broker.

PHPRedis @Github

Redis PubSub with PHP and Node.JS

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

Using Redis, you can easily listen to php events from your nodejs instance.

I suggest that you also think about the future scaling of your system and give a try at learning more nodejs to be able to move on from php.

like image 109
EMX Avatar answered Sep 29 '22 14:09

EMX


I understand your concern as your whole project has more concentration of PHP code as compared to other frameworks/languages. In order to rectify your problem, here is the Socket.io implementation for PHP v5.3 and above https://github.com/walkor/phpsocket.io.

With the help of this, you can use socket.io library in your PHP code. Below you can see an example of using Socket.io library in PHP.

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();
like image 42
Abhishek Singh Avatar answered Sep 29 '22 16:09

Abhishek Singh


You can create multiple web sockets channel. In your case, you have added one using socket.io in NodeJS. You can add another channel through php way.

You can listen to that channel the same way your are listening from NodeJS.

Few handy links 1. http://php.net/manual/en/book.sockets.php 2. How to create websockets server in PHP

like image 45
Nishant Avatar answered Sep 29 '22 16:09

Nishant