Byte side: Can TCP and UDP use the same port?

2022.07.26
Byte side: Can TCP and UDP use the same port?

In the data link layer, the host in the local area network is found by the MAC address. In the Internet layer, IP addresses are used to find interconnected hosts or routers in the network. In the transport layer, addressing by ports is required to identify different applications communicating simultaneously on the same computer.

Hello everyone, my name is Xiaolin.

A reader was asked in the byte interview before: Can TCP and UDP listen to the same port at the same time?

picture

There are still a lot of knowledge points about ports that can be said, such as these questions:

  • Can multiple TCP service processes bind to the same port at the same time?
  • Can the client's port be reused?
  • The client TCP connection has too many TIME_WAIT states, will port resources be exhausted and new connections cannot be established?

So, this time I will share these questions with you.

Can TCP and UDP bind the same port at the same time?

In fact, I feel that the question "Can TCP and UDP listen to the same port at the same time?" There is a problem with the expression. This question should be expressed as "Can TCP and UDP be bound to the same port at the same time?"

Because the action of "listening" is only available in TCP server network programming, and there is no "listening" action in UDP server network programming.

One of the similarities between TCP and UDP server networks is that bind is called to bind the port.

Let everyone know the difference between TCP and UDP network programming.

The TCP network programming is as follows. The server executes the listen() system call to monitor the port.

picture

TCP network programming

The UDP network programming is as follows. The server does not monitor this action, but only executes the bind() system call to bind the port.

picture

UDP network programming

Can TCP and UDP bind the same port at the same time?

Answer: Yes.

In the data link layer, the host in the local area network is found by the MAC address. In the Internet layer, IP addresses are used to find interconnected hosts or routers in the network. In the transport layer, addressing by ports is required to identify different applications communicating simultaneously on the same computer.

Therefore, the role of the "port number" of the transport layer is to distinguish the data packets of different applications on the same host.

The transport layer has two transport protocols, TCP and UDP, which are two completely independent software modules in the kernel.

When the host receives the data packet, it can know that the data packet is TCP/UDP in the "Protocol Number" field of the IP header, so it can determine which module (TCP/UDP) to send to the TCP/UDP module according to this information. The message is determined according to the "port number" to which application to process.

picture

Therefore, the respective port numbers of TCP/UDP are also independent of each other. For example, TCP has a port 80, and UDP can also have a port 80, and the two do not conflict.

Validation results

I simply wrote programs for TCP and UDP servers, both of which are bound to the same port number 8888.

picture

After running these two programs, you can see through the netstat command that TCP and UDP can be bound to the same port number at the same time.

picture

Can multiple TCP service processes bind to the same port?

Taking the previous TCP server program as an example, start two TCP service processes that are bound to the same port at the same time.

After running the first TCP service process, the netstat command can check that port 8888 has been bound and monitored by a TCP service process, as shown below:

picture

Then, when running the second TCP service process, an error of "Address already in use" is reported, as shown below:

picture

My test case above is that two TCP service processes bind the address and port at the same time: 0.0.0.0 address and port 8888, so the error occurs.

If the IP addresses bound to the two TCP service processes are different and the ports are the same, they can be bound successfully, as shown in the following figure:

picture

Therefore, by default, the answer to the question "Can multiple TCP service processes bind to the same port?" is: If the IP addresses and ports bound to the two TCP service processes at the same time are the same, then execute bind() When an error occurs, the error is "Address already in use".

Note that if the address bound by TCP service process A is 0.0.0.0 and port 8888, and if the address bound by TCP service process B is 192.168.1.100 address (or other addresses) and port 8888, then when executing bind() will go wrong.

This is because the 0.0.0.0 address is special and represents any address, which means that the 0.0.0.0 address is bound, which is equivalent to binding all IP addresses on the host.

Why is there an error message of "Address in use" when restarting the TCP service process?

The TCP service process needs to bind an IP address and a port, and then listen on this address and port, waiting for the arrival of client connections.

Then in practice, we may often encounter a problem. When the TCP service process is restarted, it always encounters the "Address in use" error message. The TCP service process cannot be restarted quickly, but it will take a while. The restart was successful.

Why is this?

When we restart the TCP service process, it means that the connection close operation is initiated through the server side, so it will go through four waves, and for the active closing party, it will stay in the state of TIME_WAIT for a period of time, which is about 2MSL.

picture

When the TCP service process is restarted, a connection in the TIME_WAIT state will appear on the server side. The IP+PORT used by the connection in the TIME_WAIT state is still considered to be a valid IP+PORT combination, and the same machine cannot be performed on the IP+PORT combination. Binding, then when the bind() function is executed, the error of Address already in use will be returned.

After the connection in the TIME_WAIT state ends, restarting the TCP service process will succeed.

How to avoid the "Address in use" error message when restarting the TCP service process?

We can solve this problem by setting the SO_REUSEADDR property on the socket before calling bind.

int on = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
  • 1.
  • 2.

Because the function of SO_REUSEADDR is: if the IP+PORT bound by the currently started process conflicts with the IP+PORT occupied by the connection in the TIME_WAIT state, but the newly started process uses the SO_REUSEADDR option, then the process can bind successfully.

For example, the server has a TCP server process listening on address 0.0.0.0 and port 8888. ‍

picture

A client (IP address: 192.168.1.100) has established a TCP connection with the server (IP address: 172.19.11.200), then when the TCP service process restarts, the server will wave hands with the client four times. The TCP connection is briefly in the TIME_WAIT state:

客户端地址:端口           服务端地址:端口        TCP 连接状态
192.168.1.100:37272     172.19.11.200:8888    TIME_WAIT
  • 1.
  • 2.

If the TCP service process does not set the SO_REUSEADDR attribute on the socket, then when restarting, because there is a connection in the same TIME_WAIT state as the bound IP+PORT, when the bind() function is executed, it will return Address already in use mistake.

If the TCP service process sets the SO_REUSEADDR attribute on the socket, then when restarting, even if there is a connection in the same TIME_WAIT state as the bound IP+PORT, it can still be bound successfully, so it can be successfully restarted normally.

Therefore, in all TCP server programs, it is better to set the SO_REUSEADDR attribute on the socket before calling bind, which will not cause harm, on the contrary, it will help us restart the server program in a very short time. ‍

I mentioned this problem earlier: if the address bound to TCP service process A is 0.0.0.0 and port 8888, and if the address bound to TCP service process B is 192.168.1.100 address (or other addresses) and port 8888, then There is also an error when executing bind().

This problem can also be solved by SO_REUSEADDR, because its other function is: when the bound IP address + port, as long as the IP address is not exactly the same (exactly), then the binding is allowed. ​

For example, 0.0.0.0:8888 and 192.168.1.100:8888, although the former includes the latter in a logical sense, 0.0.0.0 refers to all local IPs in general, and 192.168.1.100 refers to a specific IP, the two are not exactly the same , so after the SO_REUSEADDR attribute is set on the socket, the binding will be successful when the bind() is executed.

Can the client's port be reused?

When the client executes the connect function, it randomly selects a port in the kernel, sends a SYN message to the server, and then handshakes three times with the server.

picture

Therefore, the port selection of the client occurs in the connect function. When the kernel selects a port, it will select a port as the client port from the range specified by the kernel parameter net.ipv4.ip_local_port_range.

The default value of this parameter is 32768 61000, which means that the total number of ports available is 61000 - 32768 = 28232.

When the client and server complete the establishment of the TCP connection, we can view the TCP connection through the netstat command.

$ netstat -napt
协议  源ip地址:端口            目的ip地址:端口         状态
tcp  192.168.110.182.64992   117.147.199.51.443     ESTABLISHED
  • 1.
  • 2.
  • 3.

The question is, the above client has already used port 64992, so can you continue to use this port to initiate connections?

In this question, many students will say that the port can no longer be used. According to this understanding, the default port that the client can choose is 28232, which means that the client can only establish a maximum of 28232 TCP connections. If it is true In this case, the client has too few concurrent connections, so this is a misunderstanding.

The correct understanding is that the TCP connection is uniquely confirmed by the quadruple (source IP address, source port, destination IP address, destination port), then as long as one of the elements in the quadruple changes, it means different TCP connected. So if the client has established a connection with server A using port 64992, then if the client wants to establish a connection with server B, it can still use port 64992, because the kernel locates a TCP connection through quaternary ancestor information, not It will cause a connection conflict problem because the client's port number is the same.

For example, in the picture below, there are 2 TCP connections, the client on the left and the server on the right. The client uses the same port 50004 to establish a TCP connection with the two servers.

picture

Looking closely, the "destination IP addresses" in the four-tuple information of the above two TCP connections are different, one is 180.101.49.12, and the other is 180.101.49.11.

Can multiple clients bind the same port?

Although the bind function is often used in server-side network programming, it is also used on the client side.

We know earlier that when the client calls the connect function, the kernel randomly selects a port as the connection port.

And if we want to specify the port of the connection ourselves, we can use the bind function to achieve it: the client first binds a port through the bind function, and then calling the connect function will skip the port selection process and use the bind function instead. port.

For this question: Can multiple clients bind the same port?

It depends on whether the IP + PORT bound by multiple clients are the same. If they are the same, an error will occur when executing bind(), and the error is "Address already in use".

If one is bound to 192.168.1.100:6666 and the other is bound to 192.168.1.200:6666, because the IPs are different, when bind() is executed, it can be bound normally.

Therefore, if multiple clients bind to the same IP address and port at the same time, an error will occur when executing bind(), and the error is "Address already in use".

Generally speaking, it is not recommended to use the bind function on the client side. It is better to let the connect function select the port, because the port of the client is usually meaningless.

The client TCP connection has too many TIME_WAIT states, will port resources be exhausted and new connections cannot be established?

For this problem, it depends on whether the clients are all connected to the same server (the target address and the target port are the same).

If the client establishes a connection with the same server (the target address and the target port are the same), then if the client has too many connections in the TIME_WAIT state, when the port resources are exhausted, it cannot establish a connection with the server again.

However, because the port resource can be reused as long as the client connects to a different server.

Therefore, if the client establishes connections with different servers, even if the client port resources are only tens of thousands, it is no problem for the client to initiate millions of connections (of course, this process will also be limited by other resources, such as file descriptions characters, memory, CPU, etc.).

How to solve the problem that the client TCP connection has too many TIME_WAIT, resulting in the failure to establish a connection with the same server?

As we mentioned earlier, if the client establishes a connection with the same server (the target address and the target port are the same), then if the client has too many connections in the TIME_WAIT state, when the port resources are exhausted, it cannot be established with this server again. connected.

There is also a solution to this problem, that is to open the kernel parameter net.ipv4.tcp_tw_reuse.

Because after this kernel parameter is turned on, when the client calls the connect function, if the selected port is already occupied by the connection of the same quadruple, it will judge whether the connection is in the TIME_WAIT state, if the connection is in the TIME_WAIT state and If the TIME_WAIT state lasts for more than 1 second, the connection will be reused and the port can be used normally.

For example, suppose the client has established a TCP connection with the server, and the state is in the TIME_WAIT state:

客户端地址:端口           服务端地址:端口         TCP 连接状态
192.168.1.100:2222      172.19.11.21:8888     TIME_WAIT
  • 1.
  • 2.

Then the client initiated a connection with the server (172.19.11.21:8888). When calling the connect function, the kernel just selected port 2222, and then found that it was already occupied by the connection of the same quadruple:

  • If the net.ipv4.tcp_tw_reuse kernel parameter is not enabled, the kernel will select the next port, and then continue to judge until it finds a port that is not used by the connection of the same quadruple. If the port resources are exhausted and still not found, then connect The function will return an error.
  • If the net.ipv4.tcp_tw_reuse kernel parameter is enabled, it will determine whether the connection state of the quad is in the TIME_WAIT state. If the connection is in the TIME_WAIT state and the state lasts for more than 1 second, the connection will be reused, so You can use port 2222, then connect will return success.

To remind again, the net.ipv4.tcp_tw_reuse kernel parameter is turned on, and it only works when the client (connection initiator) calls the connect() function, so turning on this parameter on the server has no effect.

Summary of the process of client port selection

So far, we have roughly described how the kernel selects the port when the client executes the connect function. In order to let everyone understand the selection process of the client port better, I drew a flowchart.

picture

Summarize

Can TCP and UDP bind the same port at the same time?

OK.

The TCP and UDP transport protocols are implemented in the kernel by two completely independent software modules.

When the host receives the data packet, it can know that the data packet is TCP/UDP in the "Protocol Number" field of the IP header, so it can determine which module (TCP/UDP) to send to the TCP/UDP module according to this information. The message is determined according to the "port number" to which application to process.

Therefore, the respective port numbers of TCP/UDP are also independent of each other and do not affect each other.

Can multiple TCP service processes bind to the same port at the same time?

If the two TCP service processes are bound to the same IP address and port at the same time, an error will occur when executing bind(), and the error is "Address already in use".

If the two TCP service processes are bound to the same port, but different IP addresses, then there is no error in executing bind().

How to solve the problem that "Address already in use" is reported when the server restarts?

When we restart the TCP service process, it means that the connection close operation is initiated through the server side, so it will go through four waves, and for the active closing party, it will stay in the state of TIME_WAIT for a period of time, which is about 2MSL.

When the TCP service process is restarted, a connection in the TIME_WAIT state will appear on the server side. The IP+PORT used by the connection in the TIME_WAIT state is still considered to be a valid IP+PORT combination, and the same machine cannot be performed on the IP+PORT combination. Binding, then when the bind() function is executed, the error of Address already in use will be returned.

To solve this problem, we can set the SO_REUSEADDR property on the socket.

In this way, even if there is a connection in the same TIME_WAIT state as the binding IP+PORT, the binding can still be successful, so it can be restarted successfully.

Can the client's port be reused?

When the client executes the connect function, the kernel allows the port to be reused as long as the server connected to the client is not the same.

The TCP connection is uniquely confirmed by the quadruple (source IP address, source port, destination IP address, destination port), so as long as one of the elements in the quadruple changes, it means different TCP connections.

Therefore, if the client has established a connection with server A using port 64992, then if the client wants to establish a connection with server B, it can still use port 64992, because the kernel locates a TCP connection through quaternary ancestor information, and There will be no connection conflict due to the same port number of the client.

The client TCP connection has too many TIME_WAIT states, will port resources be exhausted and new connections cannot be established?

It depends on whether the clients are all connected to the same server (the destination address and destination port are the same).

If the client establishes a connection with the same server (the target address and the target port are the same), then if the client has too many connections in the TIME_WAIT state, when the port resources are exhausted, it cannot establish a connection with the server again. Even in this state, it is still possible to establish connections with other servers, as long as the client connects to a different server, the port is reused.

How to solve the problem that the client TCP connection has too many TIME_WAIT, resulting in the failure to establish a connection with the same server?

Open the kernel parameter net.ipv4.tcp_tw_reuse.

Because after this kernel parameter is turned on, when the client calls the connect function, if the selected port is already occupied by the connection of the same quadruple, it will judge whether the connection is in the TIME_WAIT state.

If the connection is in the TIME_WAIT state and the TIME_WAIT state lasts longer than 1 second, the connection is reused and the port can be used normally.