【问题标题】:NodeJS: Client to Client via ServerNodeJS:通过服务器的客户端到客户端
【发布时间】:2016-03-19 01:01:52
【问题描述】:

我在通过服务器将数据从客户端发送到客户端时遇到了一点问题(以避免监听客户端上的端口)。

我有这样的服务器:

var net = require("net");
var server = net.createServer(function(socket) {
    socket.on("data", function(data) {

    });
});
server.listen(port);

现在我想通过服务器将数据从一个客户端重定向到另一个客户端。但我不知道如何做到这一点,因为我不知道如何从另一个连接中处理一个连接。

有谁知道,我该怎么做?

【问题讨论】:

    标签: node.js sockets server


    【解决方案1】:

    您可以将所有客户端保存在一个简单的数组中,然后将其发送给每个客户端,而无需客户端的套接字 ID。所以每个客户只会得到所有其他人。那么您可以通过客户端 ID 来解决它:

    var net = require("net");
    var clients = {};
    var server = net.createServer(function(socket) {
    
        socket.on("connect", function ()
        {
            // Get the socket id of the connected client
            var socketId = null;
    
            // Get the client socket
            var clientSocket = null;
    
            clients[socketId] = clientSocket;
        });
    
        // Socket is the client
        socket.on("data", function(data) {
    
            var d = JSON.parse(data);
    
            if (d.client)
            {
                if (!clients[d.client.id])
                {
                    // Client doesn't exists
                    return;
                }
    
                // Send messages to the client with the saved socket
                clients[d.client.id].send(d.data);
            }
    
        });
    
        socket.on("end", function ()
        {
            // Get the socket id of the disconnected client
            var socketId = null;
    
            delete clients[socketId];
        });
    });
    server.listen(port);
    

    我知道与socket.io 合作,我强烈建议您也与此合作..

    在 socket.io 中可能是这样的:

    var io = require('socket.io')(80);
    
    // Store all your clients
    var clients = {};
    
    // Example: http://socket.io/docs/#using-with-node-http-server
    
    io.on("connection", function (socket)
    {
        // Save the client to let other client use it
        clients[socket.id] = socket;
    
        // Send all the clients except the self client
        sendClient(socket);
    
        // Client disconnected or connection somehow lost
        socket.on("disconnect", function ()
        {
            // Remove the client
            delete clients[socket.id];
        });
    
        // Simple event to proxy to another client
        socket.on("proxy", function (client)
        {
            if (clients[client.id])
                clients[client.id].emit("event", client.data);
        });
    });
    
    // Send all the clients and exclude the given socket id
    function sendClient(socket)
    {
        var list = [];
    
        for (let socketId in clients)
        {
            if (!clients.hasOwnProperty(socketId))
                continue;
    
            if (socketId == socket.id)
                continue;
    
            list.push(socketId);
        }
    
        socket.emit("clients", list);
    }
    

    客户端可能是(使用socket-io.client):

    var io = require('socket.io-client');
    
    var socket = io("http://localhost:80");
    
    socket.on("connect", function ()
    {
    
    });
    
    socket.on("clients", function (clients)
    {
        // We have all the clients
    
        clients.forEach(function (clientId)
        {
            var client = {};
            client.id = clientId;
            client.data = {blah: 1};
    
            socket.emit("proxy", client);
        });
    });
    
    socket.on("disconnect", function ()
    {
    
    });
    

    【讨论】:

    • 应该注意的是,在data 事件处理程序中尝试JSON.parse() 块是不安全的。您不能对传递给 data 事件处理程序的块的大小或内容做出任何假设。
    【解决方案2】:

    您必须将连接存储在某个地方,例如一个数组(如果您有两个以上)或一个以某种唯一标识符为键的对象,您可以使用该标识符轻松查找目标套接字。例如:

    var sockets = [];
    net.createServer(function(s) {
      // Add the socket to our connected socket list
      sockets.push(s);
    
      // Automatically remove the socket when it disconnects
      s.on('close', function() {
        sockets.splice(sockets.indexOf(s), 1);
      });
    
      // How you determine the destination socket is up to you
      var destSocket = ...;
    
      // This pipes data between the current socket and the
      // destination socket (both directions) but does not automatically
      // close one socket when the other socket closes
      s.pipe(destSocket, { end: false })
       .pipe(s, { end: false });
    }).listen(port);
    

    如果您的应用需要更灵活,您可以让它比这更复杂。例如,您可以通过在连接时从源套接字读取一行(以\n 结尾)来找到目标套接字,其中包含目标套接字的 IP 地址。然后,您可以在每个套接字的 IP 地址上键入一个对象,而不是一个数组,这样您就可以轻松找到合适的目标套接字。

    或者您可能让每个源套接字都发送“用户 ID”和请求的目标“用户 ID”。此时,您不妨将初始单行设为 JSON 字符串,后跟 \n,这样客户端就可以发送所需的任意数量的参数来实现桥接。

    使用 JSON 标头的更复杂解决方案的示例:

    var sockets = Object.create(null);
    
    net.createServer(function(s) {
      s.on('error', function(err) {
      });
    
      readJSON(s, function(err, result) {
        var dest;
    
        // Check if socket closed while reading our JSON header
        if (!s.writable)
          return;
    
        // Immediately close the socket when either:
        //   * We receive a malformed JSON header
        //   * Or the source socket id is already used
        //   * Or the destination socket is not connected
        // You could extend this check more if you wanted, for example
        // to perform authentication.
        if (err
            || typeof result !== 'object'
            || result === null
            || (typeof result.id !== 'string' && typeof result.id !== 'number')
            || (typeof result.destId !== 'string' && typeof result.destId !== 'number')
            || sockets[result.id]
            || !(dest = sockets[result.destId])
            || dest.busy)
          return s.destroy();
    
        var id = result.id;
        sockets[id] = s;
    
        // Set a flag on each socket to allow exclusive communication
        // between connected sockets
        s.busy = dest.busy = true;
    
        // Hook the sockets up together, allowing the closing of either
        // socket to close the other
        s.pipe(dest).pipe(s);
    
        s.on('close', function() {
          delete sockets[id];
        });
      });
    }).listen(port);
    
    function readJSON(s, cb) {
      var buf = '';
      (function read() {
        var chunk = s.read();
        if (chunk === null)
          return s.once('readable', read);
        for (var i = 0; i < chunk.length; ++i) {
          // 10 === LF (\n)
          if (chunk[i] === 10) {
            // Check if there is leftover data that is not
            // a part of our JSON message that we need to
            // put back onto the socket
            if (i < (chunk.length - 1))
              s.unshift(chunk.slice(i + 1));
    
            buf += chunk.toString('utf8', 0, i);
            var result = tryParseJSON(buf);
            if (result instanceof Error)
              cb(result);
            else
              cb(null, result);
            return;
          }
        }
        buf += chunk.toString('utf8');
        // TODO: implement `buf.length` check to make sure we don't
        // buffer indefinitely
      })();
    }
    
    // This is merely an optimization since v8 currently does not
    // optimize any functions containing a `try-catch` or `try-finally`.
    // By isolating the `try-catch` needed for JSON parsing, we can limit
    // the deoptimization.
    function tryParseJSON(str) {
      try {
        return JSON.parse(str);
      } catch (ex) {
        return ex;
      }
    }
    

    上述解决方案的示例客户端:

    var s = net.connect(port, function() {
      // Send our header
      s.write(JSON.stringify({
        id: '123',
        destId: '456'
      }) + '\n');
    
      s.end('Hello Dave!');
    }).on('error', function(err) {
      // This should fire when the server rejects our request
      // due to one of the request checks failing
      console.log('Uh oh: ' + err);
    });
    

    一旦此客户端连接(假设已连接另一个标识为456 的套接字),目标套接字应该会看到数据Hello Dave!。之后,两个套接字都将关闭。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多