I've stumbled across something interesting and I can't explain it, nor was Googling it productive.
I have one Express server, server 1, bound to localhost
:
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('server 1'))
app.listen(4000, 'localhost')
node 37624 user 27u IPv4 0x681653f502970305 0t0 TCP localhost:4000 (LISTEN)
I have another Express server, server 2, bound to all interfaces at 0.0.0.0
:
const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('server 2'))
app.listen(4000, '0.0.0.0')
node 37624 user 27u IPv4 0x681653f502970305 0t0 TCP localhost:4000 (LISTEN)
node 37693 user 25u IPv4 0x681653f4fdbdc005 0t0 TCP *:4000 (LISTEN)
Curling 0.0.0.0
gives a response from server 1, the one bound to localhost
, so clearly these two are conflicting.
Somehow, however, this does not throw an error one would expect, EADDRINUSE, how can that be?
The SO_REUSEADDR flag is being set on the network sockets in the OS by Node is causing this behavior. The REUSEADDR flag has a special interaction with the IPARR_ANY (aka 0.0.0.0 for IPv4) address. From the socket manual pages (reputable source):
SO_REUSEADDR
Indicates that the rules used in validating addresses supplied
in a bind(2) call should allow reuse of local addresses. For
AF_INET sockets this means that a socket may bind, except when
there is an active listening socket bound to the address.
When the listening socket is bound to INADDR_ANY with a spe‐
cific port then it is not possible to bind to this port for
any local address. Argument is an integer boolean flag.
From an article that goes into this exact problem:
Some folks don't like SO_REUSEADDR because it has a security stigma attached to it. On some operating systems it allows the same port to be used with a different address on the same machine by different processes at the same time. This is a problem because most servers bind to the port, but they don't bind to a specific address, instead they use INADDR_ANY (this is why things show up in netstat output as *.8080). So if the server is bound to *.8080, another malicious user on the local machine can bind to local-machine.8080, which will intercept all of your connections since it is more specific.
I modified some Linux test code to explicitly demonstrate this (bottom). When you run it you get the following output:
Opening 0.0.0.0 with no reuse flag:19999
Opening Loopback with no resuse flag:19999
bind: Address already in use
Correct: could not open lookpback with no reuse 19999
Opening 0.0.0.0 with with reuse flag:19999
Opening Loopback with with resuse flag:19999
Correct: could open lookpback with reuse 19999
The first test case opens a socket on the IPADDR_ANY address without the REUSEADDR flag set and when there is an attempt to open a socket on the loopback a EADDRINUSE error is thrown by 'bind' (as you originally expected). The second test case does the same thing but with the REUSEADDR flag set and the second socket is created without an error.
#include <errno.h>
#include <error.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define PORT 19999
int open_port(int any, int reuse)
{
int fd = -1;
int reuseaddr = 1;
int v6only = 1;
int addrlen;
int ret = -1;
struct sockaddr *addr;
int family = AF_INET;
struct sockaddr_in addr4 = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = any ? htonl(INADDR_ANY) : inet_addr("127.0.0.1"),
};
addr = (struct sockaddr*)&addr4;
addrlen = sizeof(addr4);
if ((fd = socket(family, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
goto out;
}
if (reuse){
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
sizeof(reuseaddr)) < 0) {
perror("setsockopt SO_REUSEADDR");
goto out;
}
}
if (bind(fd, addr, addrlen) < 0) {
perror("bind");
goto out;
}
if (any)
return fd;
if (listen(fd, 1) < 0) {
perror("listen");
goto out;
}
return fd;
out:
close(fd);
return ret;
}
int main(void)
{
int listenfd;
int fd1, fd2;
fprintf(stderr, "Opening 0.0.0.0 with no reuse flag:%d\n", PORT);
listenfd = open_port(1, 0);
if (listenfd < 0)
error(1, errno, "Couldn't open listen socket");
fprintf(stderr, "Opening Loopback with no resuse flag:%d\n", PORT);
fd1 = open_port(0, 0);
if (fd1 >= 0)
error(1, 0, "Was allowed to create an loopback with no reuse");
fprintf(stderr, "Correct: could not open lookpback with no reuse %d\n", PORT);
close(listenfd);
fprintf(stderr, "Opening 0.0.0.0 with with reuse flag:%d\n", PORT);
listenfd = open_port(1, 1);
if (listenfd < 0)
error(1, errno, "Couldn't open listen socket");
fprintf(stderr, "Opening Loopback with with resuse flag:%d\n", PORT);
fd1 = open_port(0, 1);
if (fd1 < 0)
error(1, 0, "Was not allowed to create an loopback with reuse");
fprintf(stderr, "Correct: could open lookpback with reuse %d\n", PORT);
close(fd1);
close(listenfd);
return 0;
}
Hello i think i can help you on this .
First see difference between 0.0.0.0
and localhost . Suppose if you are running your server on 0.0.0.0
this means that it will run that server which is available at that time so that's server 1 because 0.0.0.0
is nothing but it means that just run a server available to you so 0.0.0.0
know that server 1 is running that's why it is redirecting to server 1 because you have initialized on same port .
I mean if you run 0.0.0.0:4000
it will redirect to localhost:4000
because 0.0.0.0
is not a host but it's an address used to refer to all IP addresses on the same machine so 0.0.0.0
refers to 127.0.0.1:4000
it is the normal loopback address, and localhost:4000
is the hostname for 127.0.0.1:4000.
here is much more simpler explanation : 0.0.0.0:4000
---> 127.0.0.1:4000
---> localhost:4000
The kernel in Windows allows multiple applications to share a port as long as the Url is unique
You are listening to the same port 4000
with your two servers.
And if you wanna run two servers you should explicitly set two different port for each of the servers, something like this.
// server 1
app.get('/', (req, res) => res.send('server 1'))
app.listen(4000,() => console.log('server 1 listening to port 4000'))
// server 2
app.get('/', (req, res) => res.send('server 2'))
app.listen(5000, () => console.log('server 2 listening to port 5000'))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.