Python (3.8.12) UDP sockets does not distribute packets when SO_REUSEPORT option is set (Linux 3.10)

100 views Asked by At

When looking at python UDP sockets option 'SO_REUSEPORT', I found something strange. According to the article, How do SO_REUSEADDR and SO_REUSEPORT differ?, 'Additionally the kernel performs some "special magic" for SO_REUSEPORT sockets that isn't found in other operating systems: For UDP sockets, it tries to distribute datagrams evenly, for TCP listening sockets, it tries to distribute incoming connect requests (those accepted by calling accept()) evenly across all the sockets that share the same address and port combination. Thus an application can easily open the same port in multiple child processes and then use SO_REUSEPORT to get a very inexpensive load balancing.' But when I did some experiments, 'SO_REUSEPORT' does not do load balancing: only One socket receives packets. The other socket receives nothing!

On the computer, Python version is 3.8.12, OS is CentOS7 3.10.

I have two scripts, udp echo server and echo client. They are very simple: the client sends a message to the server; the server listens and prints incoming messages.

echo_client.py

import socket

bytesToSend = "Hello UDP Server".encode()
serverAddressPort = ("127.0.0.1", 20001)
localPort = 21111
bufferSize = 1024
cnt = 0
try:
    # Create a UDP socket at client side
    UDPClientSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
    # Must set SO_REUSEADDR/SO_REUSEPORT, otherwise incur exception 'Address already in use'
    # Must set before the first bind
    UDPClientSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    UDPClientSocket.bind(("", localPort))

    UDPClientSocket.sendto(f'Hello server: {cnt}'.encode(), serverAddressPort)
    cnt += 1

except Exception as e:
    print(f'An exception occurred {e}')

print('close client, bye !')
UDPClientSocket.close()

echo_server.py

import socket

localIP = "127.0.0.1"
localPort = 20001
bufferSize = 1024
msgFromServer = "Hello UDP Client"

bytesToSend = str.encode(msgFromServer)
cnt = 0

# Create a datagram socket
UDPServerSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
UDPServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
# Bind to address and ip
UDPServerSocket.bind((localIP, localPort))

print("UDP server up and listening")

# Listen for incoming datagrams

while True:

    print(f'\nwait for client msg ... {cnt}')
    cnt += 1
    try:
        bytesAddressPair = UDPServerSocket.recvfrom(bufferSize)
        message = bytesAddressPair[0]
        address = bytesAddressPair[1]
    except Exception as e:
        print(f'An exception occurred in recvfrom {e}')
        continue

    print(f'Message from Client:{message.decode()}')
    print(f'Client IP Address:{address}')

    try:
        # Sending a reply to client
        UDPServerSocket.sendto(bytesToSend, address)
    except Exception as e:
        print(f'An exception occurred in sendto {e}')

Two echo servers are started in different terminals (python echo_server.py). Then from third terminal send messages a few times (python echo_client.py). However, only the first terminal receives the messages! I would expect both servers receive messages, as 'SO_REUSEPORT' supposes to do load balancing.

first server display on terminal 1:

$ python echo/echo_server.py UDP server up and listening

wait for client msg ... 0 Message from Client:Hello server: 0 Client IP Address:('127.0.0.1', 21111)

wait for client msg ... 1 Message from Client:Hello server: 0 Client IP Address:('127.0.0.1', 21111)

wait for client msg ... 2 Message from Client:Hello server: 0 Client IP Address:('127.0.0.1', 21111)

wait for client msg ... 3 Message from Client:Hello server: 0 Client IP Address:('127.0.0.1', 21111)

wait for client msg ... 4 Message from Client:Hello server: 0 Client IP Address:('127.0.0.1', 21111)

wait for client msg ... 5 Message from Client:Hello server: 0 Client IP Address:('127.0.0.1', 21111)

wait for client msg ... 6 Message from Client:Hello server: 0 Client IP Address:('127.0.0.1', 21111)

wait for client msg ... 7 Message from Client:Hello server: 0 Client IP Address:('127.0.0.1', 21111)

wait for client msg ... 8

second server display on terminal 2:

$ python echo/echo_server.py UDP server up and listening

wait for client msg ... 0

Any idea what could go wrong? Thanks.

0

There are 0 answers