关于Nodejs官网上TCP连接实现的疑问?求大神解释【难道就木有人知道么】Nodejs

关于Nodejs官网上TCP连接实现的疑问?求大神解释【难道就木有人知道么】Nodejs

在官方API的child_proces模块示例代码中有这么一段代码father.js: var normal = require(‘child_proces’).fork(__dirname +’/child.js’, [‘normal’]); var special = require(‘child_process’).fork(__dirname +’/child.js’, [‘special’]); // Open up the server and send sockets to child var server = require(‘net’).createServer(); server.on(‘connection’, function (socket) { // if this is a VIP if (socket.remoteAddress === ‘74.125.127.100’) { special.send(‘socket’, socket); return; } // just the usual dudes normal.send(‘socket’, socket); }); server.listen(1337); 另外一个child.js文件代码如下: process.on(‘message’, function(m, socket) { console.log(‘here’); if (m === ‘socket’) { socket.end(‘You were handled as a ’ + process.argv[2] + ’ person’); } });

我的问题是当在命令行窗口运行father.js,即启动TCP监听服务。当在浏览器输入http://localhost:1337来访问本地端口时,console.log(‘here’);这句代码被执行了三次,也就是说在father.js中special.send(‘socket’, socket)被执行了三次。这是为什么呢?怎么避免这个问题?

当我通过在docs命令行下执行telnet localhost 1337来触发监听时,node控制台只打印了一次“here”。这又是为什么呢?


4 回复

根据你描述的问题,涉及到了Node.js中的child_process模块和net模块。child_process模块用于创建子进程,而net模块用于创建网络服务器。你的问题主要在于为什么在浏览器访问TCP端口时console.log('here')会被执行多次。

原因分析

  1. 浏览器行为

    • 当你在浏览器中访问http://localhost:1337时,浏览器会发起一个HTTP请求。由于HTTP协议是基于TCP的,因此每次请求都会建立一个新的TCP连接。
    • 浏览器通常会发送多个请求,例如一次完整的页面加载可能会包含HTML、CSS、JavaScript等资源的请求。每个资源请求都会创建一个新的TCP连接,因此connection事件会被触发多次。
  2. telnet行为

    • telnet是一个简单的命令行工具,用于测试TCP连接。当你使用telnet localhost 1337时,它只建立了一个TCP连接。因此,在这种情况下,connection事件只会被触发一次。

如何避免问题

如果你希望限制每个客户端只能建立一个连接,可以在接收到新的连接时关闭旧的连接,或者记录已经建立的连接数量,并进行相应的处理。

示例代码

var net = require('net');

var server = net.createServer();

let connectedSockets = [];

server.on('connection', function(socket) {
    // 如果已经有连接,则关闭当前连接
    if (connectedSockets.length > 0) {
        socket.end('Too many connections');
        return;
    }
    
    connectedSockets.push(socket);

    socket.on('end', () => {
        const index = connectedSockets.indexOf(socket);
        if (index > -1) {
            connectedSockets.splice(index, 1);
        }
    });

    // 检查远程地址
    if (socket.remoteAddress === '74.125.127.100') {
        special.send('socket', socket);
    } else {
        normal.send('socket', socket);
    }
});

const normal = require('child_process').fork(__dirname + '/child.js', ['normal']);
const special = require('child_process').fork(__dirname + '/child.js', ['special']);

server.listen(1337, () => {
    console.log('Server listening on port 1337');
});

在这个示例中,我们维护了一个connectedSockets数组来跟踪所有已连接的套接字。如果已有连接,则拒绝新的连接。这样可以确保每个客户端最多只有一个连接。

总结

浏览器在加载页面时会发起多个请求,导致多次触发connection事件。而telnet则只建立一个连接,因此只触发一次。通过维护连接状态并进行适当的处理,你可以控制每个客户端的最大连接数。


应该多请求了/和/favicon.ico,你手动屏蔽一下这个两个路径请求试试

TCP的socket中也有这个问题么,我记得node处理http请求有这个问题。如果是这样,那么socket中也无法获取浏览器的请求信息啊。

你的问题涉及到Node.js中的net模块如何处理TCP连接以及child_process模块如何分发这些连接。

为什么在浏览器中会打印三次 “here”?

当你在浏览器中访问 http://localhost:1337 时,浏览器通常会发送多个请求(如主请求及一些预加载请求),每个请求都会创建一个新的TCP连接。因此,child.js 文件中的 process.on('message', ...) 事件处理器会被触发多次,每次接收到一个新连接时,都会打印一次 “here” 并发送消息给对应的子进程。

如何避免这种现象?

如果你希望避免这种情况,可以考虑以下几种方法:

  1. 限制连接数量: 在 father.js 中设置最大连接数,超过该数量后拒绝新的连接。

    var maxConnections = 1;
    server.maxConnections = maxConnections;
    server.on('connection', function(socket) {
      if (server.connections >= maxConnections) {
        socket.destroy(); // 拒绝连接
      } else {
        // 处理正常或VIP连接
      }
    });
    
  2. 使用中间件或代理: 在应用层面处理请求,而不是直接在TCP层面上。例如,你可以使用Express这样的框架来处理HTTP请求,并决定是否将请求传递给TCP连接。

  3. 仅接受特定类型的连接: 只在需要时才将连接分配给子进程。例如,仅在检测到VIP IP地址时,才将连接发送给VIP子进程。

浏览器与telnet的区别

当你使用 telnet 连接到服务器时,它只会创建一个TCP连接。因此,child.js 中的 process.on('message', ...) 事件处理器只会在该单个连接上被触发一次。

// father.js 示例代码
var net = require('net');
var childProcess = require('child_process');

var normal = childProcess.fork(__dirname + '/child.js', ['normal']);
var special = childProcess.fork(__dirname + '/child.js', ['special']);

var server = net.createServer();

server.on('connection', function(socket) {
  if (socket.remoteAddress === '74.125.127.100') {
    special.send('socket', socket);
  } else {
    normal.send('socket', socket);
  }
});

server.listen(1337);
// child.js 示例代码
process.on('message', function(m, socket) {
  console.log('here');
  if (m === 'socket') {
    socket.end('You were handled as a ' + process.argv[2] + ' person');
  }
});

通过上述修改,你可以更好地控制连接的处理方式,并根据需求选择性地将连接分发给不同的子进程。

回到顶部