关于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”。这又是为什么呢?
根据你描述的问题,涉及到了Node.js中的child_process
模块和net
模块。child_process
模块用于创建子进程,而net
模块用于创建网络服务器。你的问题主要在于为什么在浏览器访问TCP端口时console.log('here')
会被执行多次。
原因分析
-
浏览器行为:
- 当你在浏览器中访问
http://localhost:1337
时,浏览器会发起一个HTTP请求。由于HTTP协议是基于TCP的,因此每次请求都会建立一个新的TCP连接。 - 浏览器通常会发送多个请求,例如一次完整的页面加载可能会包含HTML、CSS、JavaScript等资源的请求。每个资源请求都会创建一个新的TCP连接,因此
connection
事件会被触发多次。
- 当你在浏览器中访问
-
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” 并发送消息给对应的子进程。
如何避免这种现象?
如果你希望避免这种情况,可以考虑以下几种方法:
-
限制连接数量: 在
father.js
中设置最大连接数,超过该数量后拒绝新的连接。var maxConnections = 1; server.maxConnections = maxConnections; server.on('connection', function(socket) { if (server.connections >= maxConnections) { socket.destroy(); // 拒绝连接 } else { // 处理正常或VIP连接 } });
-
使用中间件或代理: 在应用层面处理请求,而不是直接在TCP层面上。例如,你可以使用Express这样的框架来处理HTTP请求,并决定是否将请求传递给TCP连接。
-
仅接受特定类型的连接: 只在需要时才将连接分配给子进程。例如,仅在检测到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');
}
});
通过上述修改,你可以更好地控制连接的处理方式,并根据需求选择性地将连接分发给不同的子进程。