提及:Marius Tibeica的回答是完整而伟大的,david_p评论也是如此。Rob Raisch 的回答也是如此(有趣的探索)。
https://stackoverflow.com/a/27040451/7668448
https://stackoverflow.com/a/13326769/7668448
注意
第一种方法不好!我把它留作参考!请参阅更新部分!为了好版本!并解释为什么!
坏版本
对于那些觉得这很有用的人,这里有一个实现繁忙端口处理的函数(如果端口繁忙,它将尝试下一个端口,直到找到一个不繁忙的端口)
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递归调用自己。在端口繁忙错误的情况下。每次增加端口号。
更新完全重做
回调完整版
首先,这个版本是遵循与 nodejshttp.Server.listen()
方法相同的签名的版本!
function listen(server) {
const args = Array.from(arguments);
// __________________________________ overriding the callback method (closure to pass port)
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;
}
签名:
听(服务器或ExpressApp,[端口[,主机[,积压]]][,回调])
就像按照
https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback
回调签名更改为
(端口)=> 无效
用法:
const server = listen(app, 3000, (port) => {
console.log("server is running on port :" + port);
});
// _____________ another example port and host
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;
}
这里的参数端口变量玩就关闭了!
ES6 完整版
function listen(server, ...args) {
// __________________________________ overriding the callback method (closure to pass port)
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 的评论,看看他遇到了什么问题!可以深思熟虑!
同样在评论中我提到了承诺版本和闭包吸气剂模式!我不认为他们有趣!上面的方式只是尊重与nodejs相同的签名!而且回调就好了!而且我们正在将我们的服务器参考写掉!带有承诺版本!一个承诺得到回报,我们通过了所有的元素!服务器实例+端口!
如果你想知道闭包吸气剂模式!(这里不好)
在我们的方法中,我们创建了一个引用服务器实例的 ref!如果我们不能像我们正在做的那样返回服务器实例(想象这是不可能的!所以每次创建一个新实例!该模式包括创建一个闭包(在该范围内的方法)并返回它!
所以为了使用
const getServer = listen(port, () => {
console.log('Server running at port ' + getServer().address().port);
const io = socketIo(getServer(), {});
});
但这只是开销,特别是我们需要等待服务器完成!除非我们以使用回调的方式设置它!或回报承诺!
它只是过于复杂了!而且一点都不好!
只是因为我提到过!
上面的方法可以调整!添加尝试次数限制!并添加一些事件或钩子!不过好!通常我们只需要一个简单的函数来尝试并实现它!对我来说,以上就足够了!
好的链接
从文档
app.listen() 方法返回一个 http.Server 对象,并且(对于 HTTP)是以下的便捷方法:
app.listen = function () {
var server = http.createServer(this)
return server.listen.apply(server, arguments)
}