Can a port number be bound by two processes at the same time?

1, Can a port number be bound by two processes at the same time?

According to the binding of port number, we will discuss the following situations:

  1. The two processes establish TCP server s respectively and use the same port number 8888
  2. Two processes establish UDP server s respectively and use the same port number 8888
  3. Two processes, one establishing TCP server and one establishing UDP server, all use port number 8888

1. Test code

We first write two simple test programs.

tcp.c

The program only creates a tcp socket and binds the port number 8888, does not accept the connection operation, and sleep(1000), so that the process does not exit too soon.

/*******Server program tcpserver c ************/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <pthread.h>

#define WAITBUF 10
#define RECVBUFSIZE 1024


int main(int argc, char *argv[])
{
	int sockfd,new_fd,nbytes;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	int portnumber = 8888;
	socklen_t sin_size;
	char hello[512];
	char buffer[RECVBUFSIZE];

	/*Wrong port number, exit*/

	/*The server starts to establish socket descriptor*/
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)  
	{
		fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
		exit(1);
	}

	/*Server side filling sockaddr structure*/ 
	bzero(&server_addr,sizeof(struct sockaddr_in));
	server_addr.sin_family=AF_INET;
	/*Auto populate host IP*/
	server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	server_addr.sin_port=htons(portnumber);

	/*Binding sockfd descriptor process + port number + ip+socket*/ 
	if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
	{
		fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
		exit(1);
	}

	/*Listen for sockfd descriptor*/
	if(listen(sockfd, WAITBUF)==-1)
	{
		fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
		exit(1);
	}

	sleep(1000);//Let the program not exit so quickly
	close(sockfd);
	exit(0);
}

udp.c
The program only creates udp socket and binds port number 8888. It does not accept the connection operation, and sleep(1000), so that the process does not exit too soon

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

#define SERVER_PORT 8888 
#define MAX_MSG_SIZE 1024 


int main(void) 
{ 
	int sockfd; 
	struct sockaddr_in addr; 

	/* The server starts to establish socket descriptor */ 
	sockfd=socket(AF_INET,SOCK_DGRAM,0); 
	if(sockfd<0) 
	{ 
		fprintf(stderr,"Socket Error:%s\n",strerror(errno)); 
		exit(1); 
	} 

	/* Server side filling sockaddr structure */ 
	bzero(&addr,sizeof(struct sockaddr_in)); 
	addr.sin_family=AF_INET; 
	addr.sin_addr.s_addr=htonl(INADDR_ANY); 
	addr.sin_port=htons(SERVER_PORT); 

	/* Bundle sockfd descriptor */ 
	if(bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0) 
	{ 
		fprintf(stderr,"Bind Error:%s\n",strerror(errno)); 
		exit(1); 
	} 
	sleep(1000);
	close(sockfd); 
} 

compile

gcc tcp.c -o tcp
gcc udp.c -o udp

2. Implementation results

1) . 2 processes establish TCP server s respectively


It can be seen from the results that the second process failed to bind port number 8888.

2) . 2 processes establish UDP server s respectively


It can be seen from the results that the second process failed to bind port number 8888.

3) . 1 TCP server and 1 UDP server


Use the netstat command to view the information.

The results show that in this case, the two processes are bound successfully.

3. Result analysis

It can be seen from the above results that TCP and UDP can bind a port 8888 at the same time, but a port cannot be bound twice by TCP or UDP at the same time.
The reasons are as follows:

  1. tcp port is not a physical concept, but just two bytes in the protocol stack;
  2. TCP and UDP ports have no relationship at all. It is entirely possible that there is another XXP based on IP and the concept of port, which is entirely possible;
  3. After TCP and UDP transport protocols listen to the same port, the received data will not affect or conflict with each other. Because when receiving data, the receiver is judged according to the five tuple * * {transmission protocol, source IP, destination IP, source port and destination port} * *.

2, Some other knowledge points of port number

1. Function of port number

Port number can be used to identify different applications communicating on the same host. Port number + IP address can form a socket to identify a process.

2. Application scenario of port number

In TCP/IP protocol, a communication is identified by a five tuple of "source IP address", "destination IP address", "source port number", "destination port number", and protocol number (the protocol number of IP protocol is 4 and the protocol number of TCP is 6). When both sides of communication send a message, the message header will carry such a five tuple.

3. Port range Division

(1) 0 ~ 1023: well-known port number, which is reserved for standby. One is used for protocols, such as HTTP, FTP and SSH;
(2) 1024 ~ 65535: it is the port number dynamically allocated by the operating system. The port number of the client program is allocated from this range by the operating system. In the socket communication between TCP and UDP, the port number of the client is in this range.

4. Well known port number and server corresponding to port number

For example:

	HTTP Server: 80 
	FTP Server: 21

ps: FTP has a control connection and a data connection, so FTP has two port numbers. The port number of control connection is 21 and the port number of data connection is 20. However, if the port number of FTP is 21 by default, if it indicates that FTP has two port numbers, it is 21 and 20. Otherwise, the port number of FTP server is 21

	TELNET Server: 23 
	SSH Server: 22 
	HTTPS: 443 
	WEB Server: 25

5. How to check the well-known port number in linux?

cat /etc/services

6. Can a process bind multiple port numbers?

sure

Because a process can open multiple file descriptors, and each file descriptor corresponds to a port number, a process can bind multiple port numbers.

The Linux kernel will assign a unique file descriptor to each socket, and the process will distinguish the corresponding socket through the file descriptor.

7. Can a port number be bound by multiple processes?

The same kind of agreement is usually not possible, but there is one case.

ps: if a process binds a port number first and then fork s a sub process, it can bind multiple processes to a port number, but it is not allowed for two different processes to bind the same port number.

3, So_ What is the use of reuseaddr and how to use it?

When the address and port of two sockets conflict, and we want to reuse the address and port, the old socket and the new socket must have been set_ Reuseaddr feature is only one of the two. There is still a problem with this feature.

SO_REUSEADDR can be used in the following four cases. (from Unix Network Programming Volume I, UNPv1)

  1. When a socket1 with the same local address and port is in time_ When the wait state is [4 handshakes], and the socket 2 of the program you start needs to occupy the address and port, your program needs to use this option.

Generally speaking, a port will wait for two minutes after it is released before it can be used again, SO_REUSEADDR allows the port to be reused immediately after it is released.

SO_REUSEADDR is used to set TCP socket at time_ The socket in the wait state can be bound and used repeatedly. The server program should always set so before calling bind()_ Reuseaddr socket option. TCP, the party calling close() first will enter TIME_WAIT status.

The sequence of 4 handshakes is shown in the figure below:

  1. SO_REUSEADDR allows multiple instances (multiple processes) of the same server to be started on the same port. However, the IP address bound to each instance cannot be the same. This can be tested on machines with multiple network cards or with IP Alias technology.

  2. SO_REUSEADDR allows a single process to bind the same port to multiple sockets, but the ip address of each socket is different. This is very similar to 2. See UNPv1 for the difference.

SO_REUSEADDR allows you to start a listening server and bundle its well-known port, even if the previously established connection using this port as their local port still exists. This usually occurs when the listening server is restarted. If this option is not set, an error will occur during bind.

SO_REUSEADDR allows you to start multiple instances of the same server on the same port, as long as each instance is bundled with a different local IP address. For TCP, it is impossible to start multiple servers with the same IP address and port number.

SO_REUSEADDR allows a single process to bind the same port to multiple sockets, as long as each bundle specifies a different local IP address. This is not generally used for TCP servers.

  1. SO_REUSEADDR allows duplicate binding of identical addresses and ports. But this is only used for UDP multicast, not TCP.

SO_REUSEADDR allows complete and repeated bundling: when an IP address and port are bound to a socket, it also allows this IP address and port to be bound to another socket. Generally speaking, this feature is only available on systems that support multicast, and only for UDP sockets (TCP does not support multicast).

SO_ The reuseport option has the following semantics:
This option allows full duplicate bundling, but only if the socket option is specified for all sockets that want to bundle the same IP address and port.

If the bundled IP address is a multicast address, SO_REUSEADDR and SO_REUSEPORT is equivalent.

Recommendations for using these two socket options:
In all TCP servers, set so before calling bind_ Reuseaddr socket option;
Set so when writing a multicast application that can run multiple times on the same host at the same time_ Reuse addr option, and bind the multicast address of this group as the local IP address.

The setting method is as follows:

if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
   (const void *)&nOptval , sizeof(int)) < 0) 
   ...

attach
Q: Write TCP / sock_ When the stream service program, so_ What exactly does reuseaddr mean?

A: This socket option informs the kernel if the port is busy but the TCP status is at TIME_WAIT, ports can be reused. If the port is busy and the TCP state is in other states, you will still get an error message indicating that "the address is in use" when reusing the port. If you want to restart immediately after your service program stops, and the new socket still uses the same port, so_ The reuse addr option is very useful. It must be realized that any unexpected data arrival at this time may lead to confusion in the response of the service program, but this is only a possibility. In fact, it is very impossible.

A socket consists of five related tuples: protocol, local address, local port, remote address and remote port. SO_REUSEADDR only means that the local address and local port can be reused. The whole related quintuple is still unique. Therefore, the restarted service program may receive unexpected data. So must be used with caution_ Reuseaddr option.

Example 1: test the first case above.

#include <netinet/in.h> 
#include <sys/socket.h> 
#include <time.h> 
#include <stdio.h> 
#include <string.h> 
#define MAXLINE 100 

int main(int argc, char** argv) 
{ 
   int listenfd,connfd; 
   struct sockaddr_in servaddr; 
   char buff[MAXLINE+1]; 
   time_t ticks; 
   unsigned short port; 
   int flag=1,len=sizeof(int); 

   port=10013; 

   if( (listenfd=socket(AF_INET,SOCK_STREAM,0)) == -1) 

   { 
     perror("socket"); 
     exit(1); 
   } 

   bzero(&servaddr,sizeof(servaddr)); 
   servaddr.sin_family=AF_INET; 
   servaddr.sin_addr.s_addr=htonl(INADDR_ANY); 
   servaddr.sin_port=htons(port); 

   if( setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1) 
  { 
      perror("setsockopt"); 
      exit(1); 
   } 

   if( bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1) 
   { 
      perror("bind"); 
      exit(1); 
   } 
   else 
      printf("bind call OK!\n"); 
      
   if( listen(listenfd,5) == -1) 
   { 
      perror("listen"); 
      exit(1); 
   } 

   for(;;) 
   { 
      if( (connfd=accept(listenfd,(struct sockaddr*)NULL,NULL)) == -1)
      { 
          perror("accept"); 
          exit(1); 
      } 
      if( fork() == 0)/*child process*/ 
      { 
        close(listenfd);/*Close the listening socket, which is not required by the child process.*/ 
        
        ticks=time(NULL); 
        snprintf(buff,100,"%.24s\r\n",ctime(&ticks)); 
        
        write(connfd,buff,strlen(buff)); 
        close(connfd); 
        
        sleep(1); 
        
        execlp("run",NULL); 
        perror("execlp"); 
        exit(1); 
     } 
     close(connfd); 
     exit(0);/* end parent*/ 
  } 
} 
gcc 123.c -o run
sudo cp run /sbin
sudo chmod 777 /sbin/run

Test: compile it into a run program, put it in a PATH in your own PATH environment variable, such as $HOME/bin, run run it, and then telnet localhost 10013 to see the results.

First step
Run the program, and the program is blocked in the position of accept().

Step two
Reopen a terminal and execute the following command.

Step 3:
You can see that the program running asynchronously exits and prints bind call OK!
It indicates that the subprocess is executed, and the port 10013 is successfully bound. The first case is verified.

  1. In the second case, I have no environment test, so I won't give the test program. If you have conditions, you can write one yourself to test.

  2. Procedure for testing the third case
    Read local ip address

eth0 :  192.168.43.171
lo      : 127.0.0.1

#include <netinet/in.h> 
#include <sys/socket.h> 
#include <time.h> 
#include <stdio.h> 
#include <string.h> 
#define MAXLINE 100 

int main(int argc, char** argv) 
{ 
   int fd1,fd2; 
   struct sockaddr_in servaddr1,servaddr2; 
   char buff[MAXLINE+1]; 
   time_t ticks; 
   unsigned short port; 
   int flag=1,len=sizeof(int); 

   port=10013; 

   if( (fd1=socket(AF_INET,SOCK_STREAM,0)) == -1) 
   { 
       perror("socket"); 
       exit(1); 
   } 

   if( (fd2=socket(AF_INET,SOCK_STREAM,0)) == -1) 
   { 
       perror("socket"); 
       exit(1); 
   } 

   bzero(&servaddr1,sizeof(servaddr1)); 
   bzero(&servaddr2,sizeof(servaddr2)); 
   servaddr1.sin_family=AF_INET; 
   servaddr2.sin_family=AF_INET; 

   if( inet_pton(AF_INET, "127.0.0.1", &servaddr1.sin_addr) <= 0) 
	{ 
		printf("inet_pton() call error:127.0.0.1\n"); 
		exit(1); 
	} 

	if( inet_pton(AF_INET, "192.168.43.171", &servaddr2.sin_addr) <= 0) 
	{ 
		printf("inet_pton() call error:128.160.1.230\n"); 
		exit(1); 
	} 

	servaddr1.sin_port=htons(port); 
	servaddr2.sin_port=htons(port); 
	
	if( setsockopt(fd1, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1) 
	{ 
		perror("setsockopt"); 
		exit(1); 
	} 

	if( setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1) 
	{ 
		perror("setsockopt"); 
		exit(1); 
	} 

	if( bind(fd1,(struct sockaddr*)&servaddr1,sizeof(servaddr1)) == -1)
	{ 
		perror("bind fd1"); 
		exit(1); 
	} 

	if( bind(fd2,(struct sockaddr*)&servaddr2,sizeof(servaddr2)) == -1)
	{ 
		perror("bind fd2"); 
		exit(1); 
	} 

	printf("bind fd1 and fd2 OK!\n"); 
	
	/*put other process here*/ 
	getchar(); 
	exit(0);/* end */ 
} 

results of enforcement

  1. Since the fourth case is only used for UDP multicast and has little to do with the use of TCP, we won't write a test example. You can write if you are interested.

For more embedded Linux knowledge, please pay attention to a mouthful of Linux

Tags: Linux network socket

Posted by Collin on Sun, 08 May 2022 07:17:03 +0300