skip to Main Content

I have an express server set up as follows to download torrents:

Server Side

const WebTorrent = require('webtorrent')
const client = new WebTorrent()
const express = require('express')
const app = express()
const port = 3001
const http = require('http')
const server = http.createServer(app)
const { Server } = require("socket.io");
const io = new Server(server, {
    cors: {
        origin: "http://localhost:3000"
    }
});
app.io = io

//emitting works fine, but if more than one user sends a request to this endpoint, everyone gets all messages
app.get('/test', (req, res) => {
   const link = req.query.magnetlink
   req.app.io.emit('started', true)
   client.add(link, (torrent) => {
       const name = torrent.name
       req.app.io.emit('started', true)
       torrent.on('download, () => {
           req.app.io.emit('data', () => {
                name: name,
           })
       })
       torrent.on('done', () => {
           req.app.io.emit('done', () => {
                name: name
           })
       })
   })
})
server.listen(port, () => {
  console.log(`Running on http://localhost:${port}`)
})

React Client Side

import * as React from 'react';
React.useEffect(() => {
   socket.on('started', (data) => {
       console.log(data)
       //do something here
   })
   socket.on('data', (data) => {
      console.log(data)
      //do something here
   })
}, [])

The post I got the base code from is almost 7 years old and doesn’t have much information on how to do these things, along with the socket.io docs that doesn’t really have any good information on this either.

There are two problems here. The first one is that all users are receiving the same messages which is a problem for my ui which adds the data to a state and it renders a part of the page with the new data. So I need every user who gets connected to get only their data that they requested.

The second problem is that using this method, I have no way of telling if the user is still connected to the route. If they are still connected I want to continue sending data, and if they aren’t I want to kill the process the server is doing to retrieve and send that data. Even emitting to the server inside of one of the ‘socket.on’ never gets received by the server. How do I go about checking if the client is still connected so I don’t waste bandwidth or waste storage space? I can’t use io.on(‘connect’) inside the route because I use it elsewhere to check if a user is online and count how many users are online. I just want to know if the user is still connected to the /test route specifically. Again, emitting works fine. I also want the messages to only be sent to that specific user connected to the /test route WHILE they are connected to the /test route. If the user refreshes their page or cancels I want to stop the data transfer on the server side which is doing things on its own.

2

Answers


  1. Chosen as BEST ANSWER

    I figured out my own solution. The steps are as follows:

    1. I first sent a request to the server to /handshake, which verified the user using auth and other middlewares. This would return an OK if the user was okay to download, if not, then send whatever error (403, 500, etc).
    2. Once the client got the handshake, then I would emit a 'download' signal to socket.io with all the download options required.
    3. As the file is downloading on the server, the user would get status updates from the 'on.('download')' from the torrent.
    4. If at any time the client disconnects, the download is killed and the files deleted.
    5. Once the download is done, it is zipped and the 'torrentDone' signal is emitted to the client with file information so the user can find it later.
    6. Then whenever the user is ready, they just send the request to the server using a normal express endpoint and the file is served then deleted on the server.

    Thanks for the help.


  2. The first one is that all users are receiving the same messages which is a problem for my ui which adds the data to a state and it renders a part of the page with the new data.

    That’s because you’re doing io.emit(). That broadcasts to all connected clients. You need to do socket.emit() to the specific socket you want to send to.

    The second problem is that using this method, I have no way of telling if the user is still connected to the route

    Uses are never connected to a route. An express route is a temporal thing. Some client makes an http request to a route. The response gets sent. The http request/route is now over. There is no connection to a route.

    A client can make a socket.io connection to a server. That makes a continuous connection between client and server that has NOTHING at all to do with a specific route. That’s a connection between that specific client and the server.

    If you use that socket.io connection to then request the download, then when you get the result of that download, you can send it back over the same socket that requested it. You don’t have to try to match a socket with a route at all. You get a request on the socket and you send the response back on the same socket.

    I don’t know your torrent code, but the general structure would be like this:

    // client sends socket.io getDownload message with {url: xxx} as the data to 
    // initiate your torrent code
    
    // server-side code
    io.on('connection', socket => {
        socket.on('getDownload', data => {
            const url = data.url;
            client.add(url, (torrent) => {
                const name = torrent.name
                socket.emit('started', true);
    
                // other torrent code here
                // you can use socket.emit() here to send to this particular socket
                // When torrent is done, make sure to do client.remove() to clean up
    
            });
        });
        socket.on('disconnect', () => {
            // clean up any torrent stuff on behalf of this client
        });
    });
    

    Do not initiate your torrent stuff with the http URL because you don’t know which socket.io connection belongs to that client making that http request or if there even is one yet.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search