Port reuse So_Reuseaddr

Port reuse So_Reuseaddr

Port reuse is a classic problem in network programming, and the knowledge points here are very cumbersome. This article briefly introduces SO_REUSEADDR through code, but does not involve SO_REUSEPORT.

For a long time, we have all known that we cannot listen to the same port. For example, the following code.

 server1 .listen ( 8080 ) ;
server2 .listen ( 8080 ) ;

We will see the error Address already in use. But is it really impossible to bind to the same port? Not necessarily.

 #include < stdio .h >
#include < stdlib .h >
#include < string .h >
#include < errno .h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < unistd .h >
#include < arpa / inet.h >

void start_server ( __uint32_t host ) {
int listenfd , connfd ;
struct sockaddr_in servaddr ;

if ( ( listenfd = socket ( AF_INET , SOCK_STREAM , 0 ) ) == - 1 ) {
goto ERROR ;
}
memset ( & servaddr , 0 , sizeof ( servaddr ) ) ;
servaddr .sin_family = AF_INET ;
servaddr .sin_addr .s_addr = host ;
servaddr .sin_port = htons ( 6666 ) ;

if ( bind ( listenfd , ( struct sockaddr * ) & servaddr , sizeof ( servaddr ) ) == - 1 ) {
goto ERROR ;
}

if ( listen ( listenfd , 10 ) == - 1 ) {
goto ERROR ;
}
return ;
ERROR :
printf ( "bind socket error: %s(errno: %d)\n" , strerror ( errno ) , errno ) ;
}

int main ( ) {
start_server ( inet_addr ( "127.0.0.1" ) ) ;
start_server ( inet_addr ( "192.168.8.246" ) ) ;
}

The above code starts two servers, both of which are bound to the same port. The compilation and execution can run normally because I specified different IPs. It can be seen that we usually think that multiple servers cannot listen to the same port at the same time because we only specify the port but not the IP.

 const net = require ( 'net' ) ;
const server = net .createServer ( ) ;
server .listen ( 8080 ) ;

Execute the above code and you can see the bound address *:8080 through lsof -i:8080. In other words, if we do not specify an IP, the system will listen to all IPs by default. When listening to the same port for the second time, an error will be reported. Let's look at the second case.

 #include < stdio .h >
#include < stdlib .h >
#include < string .h >
#include < errno .h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < unistd .h >
#include < arpa / inet.h >

void start_server ( __uint32_t host ) {
int listenfd , connfd ;
struct sockaddr_in servaddr ;

if ( ( listenfd = socket ( AF_INET , SOCK_STREAM , 0 ) ) == - 1 ) {
goto ERROR ;
}
memset ( & servaddr , 0 , sizeof ( servaddr ) ) ;
servaddr .sin_family = AF_INET ;
servaddr .sin_addr .s_addr = host ;
servaddr .sin_port = htons ( 6666 ) ;

if ( bind ( listenfd , ( struct sockaddr * ) & servaddr , sizeof ( servaddr ) ) == - 1 ) {
goto ERROR ;
}

if ( listen ( listenfd , 10 ) == - 1 ) {
goto ERROR ;
}
return ;
ERROR :
printf ( "bind socket error: %s(errno: %d)\n" , strerror ( errno ) , errno ) ;
}

int main ( ) {
start_server ( htonl ( INADDR_ANY ) ) ;
start_server ( inet_addr ( "127.0.0.1" ) ) ;
}

The above code will report an error "Address already in use" when executed. Why does it not work when it is changed to INADDR_ANY? Because INADDR_ANY represents all IPs, so it cannot be bound to other IPs by default. Logically, when the operating system receives the data packet of 127.0.0.1:6666, it does not know who to give it to because both bound addresses are hit. But we can tell the operating system to whom to give this data packet.

 #include < stdio .h >
#include < stdlib .h >
#include < string .h >
#include < errno .h >
#include < sys / types.h >
#include < sys / socket.h >
#include < netinet / in .h >
#include < unistd .h >
#include < arpa / inet.h >

void start_server ( __uint32_t host ) {
int listenfd , connfd ;
struct sockaddr_in servaddr ;

if ( ( listenfd = socket ( AF_INET , SOCK_STREAM , 0 ) ) == - 1 ) {
goto ERROR ;
}
int on = 1 ;
if ( setsockopt ( listenfd , SOL_SOCKET , SO_REUSEADDR , & on , sizeof ( on ) ) ) {
goto ERROR ;
}

memset ( & servaddr , 0 , sizeof ( servaddr ) ) ;
servaddr .sin_family = AF_INET ;
servaddr .sin_addr .s_addr = host ;
servaddr .sin_port = htons ( 6666 ) ;

if ( bind ( listenfd , ( struct sockaddr * ) & servaddr , sizeof ( servaddr ) ) == - 1 ) {
goto ERROR ;
}

if ( listen ( listenfd , 10 ) == - 1 ) {
goto ERROR ;
}
return ;
ERROR :
printf ( "bind socket error: %s(errno: %d)\n" , strerror ( errno ) , errno ) ;
}

int main ( ) {
start_server ( htonl ( INADDR_ANY ) ) ;
start_server ( inet_addr ( "127.0.0.1" ) ) ;
}

The above code adds the logic of SO_REUSEADDR and compiles and executes successfully. It can be seen that SO_REUSEADDR tells the operating system who should handle a data packet when it hits multiple sockets. After the operating system has clarified this logic, it naturally allows the port to be listened in this way.

<<:  It took two years for 5G messaging to be officially commercialized. Is that it?

>>:  Expectations for Network as a Service (NaaS) Technology

Recommend

With the launch of 5G and Wi-Fi 6, where will wireless network products go?

Today, topics about 5G and Wi-Fi are endless, and...

AT&T requires all hardware vendors to support Open RAN specifications

According to Light Reading, executives of US tele...

2021 Information and Communication Industry Events

ICT industry recovers According to statistics fro...

Ethernet in IoT Still Has a Role in the Wireless Era

When it comes to the Internet and cloud computing...

China Mobile and Huawei jointly complete 5G voice full function testing

Recently, China Mobile and Huawei jointly complet...

What is 6G and how close are we to its launch?

No, you read that correctly - 6G. Considering tha...

WOT2018 Xian Yunsen: Algorithms are everywhere in O2O search

[51CTO.com original article] Seven years of hard ...

5G commercialization is accelerating. What does this mean for drones?

Since the beginning of this year, my country'...