提到: Marius Tibeica的答案很完整和出色,david_p的评论也是。Rob Raisch的答案也不错(有趣的探索)。https://dev59.com/GWYr5IYBdhLWcg3w499-#27040451
https://dev59.com/GWYr5IYBdhLWcg3w499-#13326769
注意
这个第一种方法是糟糕的!我将其保留作为参考!请看更新部分!了解更好的版本!也了解更好的解释!
糟糕版本
对于那些会发现这个函数有用的人们,在这里有一个实现忙端口处理的函数(如果端口忙碌,则会尝试下一个端口,直到找到没有忙碌的端口)。
app.portNumber = 4000;
function listen(port) {
app.portNumber = port;
app.listen(port, () => {
console.log("server is running on port :" + app.portNumber);
}).on('error', function (err) {
if(err.errno === 'EADDRINUSE') {
console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
listen(port + 1)
} else {
console.log(err);
}
});
}
listen(app.portNumber);
当端口繁忙时,函数 listen 会对自身进行递归调用,并每次将端口号加一。
完全重新做过的更新
回调函数完整版本
首先,这个版本遵循与 Node.js 中 http.Server.listen()
方法相同的签名!
function listen(server) {
const args = Array.from(arguments);
const lastArgIndex = arguments.length - 1;
let port = args[1];
if (typeof args[lastArgIndex] === 'function') {
const callback = args[lastArgIndex];
args[lastArgIndex] = function () {
callback(port);
}
}
const serverInstance = server.listen.apply(server, args.slice(1))
.on('error', function (err) {
if(err.errno === 'EADDRINUSE') {
console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
port += 1;
serverInstance.listen.apply(serverInstance, [port].concat(args.slice(2, lastArgIndex)));
} else {
console.log(err);
}
});
return serverInstance;
}
签名:
listen(serverOrExpressApp, [port[, host[, backlog]]][, callback])
与https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback相同。
回调函数的签名已更改为
(port) => void
用法:
const server = listen(app, 3000, (port) => {
console.log("server is running on port :" + port);
});
const server = listen(app, 3000, 'localhost', (port) => {
console.log("server is running on port :" + port);
});
说明
与旧示例相反!此方法不会调用自身!
关键要素:
- app.listen() 的第一个调用将返回一个 net.Server 实例
- 在绑定事件后,再次调用 listen 到同一 net.Server 实例将尝试重新连接!
- 错误事件侦听器始终存在!
- 每次发生错误时,我们都会重新尝试。
- 端口变量在回调的闭包中起作用!当回调被调用时,正确的值将被传递。
重要的是
serverInstance.listen.apply(serverInstance, [port].concat(args.slice(2, lastArgIndex)));
为什么我们在这里跳过了回调函数!?
一旦添加了回调函数!它会在服务器实例内部保留在一个数组中!如果我们添加另一个!就会有多个触发器!次数为(尝试次数+1)。所以我们只在第一次尝试中包含它!
这样我们就可以直接返回服务器实例!并继续使用它来尝试!并且这样做很干净!
仅简单版本端口
这也可以帮助更好地一览
function listen(server, port, callback) {
const serverInstance = server.listen(port, () => { callback(port) })
.on('error', function (err) {
if(err.errno === 'EADDRINUSE') {
console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
port += 1;
serverInstance.listen(port);
} else {
console.log(err);
}
});
return serverInstance;
}
这里的参数port变量在闭包中发挥作用!
ES6全版本
function listen(server, ...args) {
const lastArgIndex = args.length - 1;
let port = args[0];
if (typeof args[lastArgIndex] === 'function') {
const callback = args[lastArgIndex];
args[lastArgIndex] = function () {
callback(port);
}
}
const serverInstance = server.listen(server, ...args)
.on('error', function (err) {
if(err.errno === 'EADDRINUSE') {
console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
port += 1;
serverInstance.listen(...[port, ...args.slice(1, lastArgIndex)])
} else {
console.log(err);
}
});
return serverInstance;
}
为什么旧版本不好
实际上并不是那样!但是在第一个版本中!每次失败时我们都调用函数本身!而且每次它都创建一个新的实例!垃圾回收器将要忙碌一些!
这并不重要,因为这个函数只执行一次,在开始时执行!
旧版本没有返回服务器实例!
附加信息(针对@sakib11)
您可以查看@sakib11的评论,了解他遇到的问题!这可能很有启发性!
还在评论中,我提到了promise版本和闭包getter模式!我认为它们并不有趣!以上方法只需遵守与nodejs相同的签名即可!而太多的回调也没问题!我们立即获得了服务器引用!通过promise版本!会返回一个promise,解决后我们传递所有元素!serverInstance + 端口!
如果您想了解闭包getter模式!(在这里不好)
在我们的方法内部,我们创建一个引用,引用服务器实例!如果我们无法像现在这样返回服务器实例(假设不可能!所以每次都会创建一个新实例!),则该模式包括创建一个闭包(该范围内的方法)并返回它!
因此,对于用法:
const getServer = listen(port, () => {
console.log('Server running at port ' + getServer().address().port);
const io = socketIo(getServer(), {});
});
但这只是额外的开销,特别是我们需要等待服务器完成!除非我们以使用回调函数或返回 Promise 的方式设置它!
这只会使问题变得更加复杂!根本不好!
只是因为我提到了它!
而且上述方法可以进行调整!添加尝试次数限制!并添加一些事件或钩子!但是嗯!通常我们只需要一个简单的函数来尝试并使其成功!对我来说,以上内容已经足够了!
好链接
来自文档
app.listen() 方法返回一个 http.Server 对象,(对于 HTTP)它是以下方便方法:
app.listen = function () {
var server = http.createServer(this)
return server.listen.apply(server, arguments)
}
listener.on 'error', ...
应该可以工作。即使有这行代码,它是否只会执行正常的堆栈跟踪并崩溃呢? - loganfsmyth