Question: I know this topic is not new. There is various information out there although, the robust solution is not presented (at least I did not found). I have a P2P daemon written in python3 and the last element on the pie is to connect two clients behind the NAT via TCP. My references for this topic: https://bford.info/pub/net/p2pnat/ How to make 2 clients connect each other directly, after having both connected a meeting-point server? Problems with TCP hole punching What I have done so far:  SERVER: #!/usr/bin/env python3 import threading import socket MY_AS_SERVER_PORT = 9001 TIMEOUT = 120.0 BUFFER_SIZE = 4096 def get_my_local_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # doesn't even have to be reachable s.connect(('10.255.255.255', 1)) IP = s.getsockname()[0] except Exception: IP = '127.0.0.1' finally: s.close() return bytes(IP, encoding='utf-8') def wait_for_msg(new_connection, client_address): while True: try: packet = new_connection.recv(BUFFER_SIZE) if packet: msg_from_client = packet.decode('utf-8') client_connected_from_ip = client_address[0] client_connected_from_port = client_address[1] print("We have a client. Client advertised his local IP as:", msg_from_client) print(f"Although, our connection is from: [{client_connected_from_ip}]:{client_connected_from_port}") msg_back = bytes("SERVER registered your data. Your local IP is: " + str(msg_from_client) + " You are connecting to the server FROM: " + str(client_connected_from_ip) + ":" + str(client_connected_from_port), encoding='utf-8') new_connection.sendall(msg_back) break except ConnectionResetError: break except OSError: break def server(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) sock.bind((get_my_local_ip().decode('utf-8'), MY_AS_SERVER_PORT)) sock.listen(8) sock.settimeout(TIMEOUT) while True: try: new_connection, client_address = sock.accept() if new_connection: threading.Thread(target=wait_for_msg, args=(new_connection,client_address,)).start() # print("connected!") # print("") # print(new_connection) # print("") # print(client_address) msg = bytes("Greetings! This message came from SERVER as message back!", encoding='utf-8') new_connection.sendall(msg) except socket.timeout: pass if __name__ == '__main__': server()  CLIENT: #!/usr/bin/python3 import sys import socket import time import threading SERVER_IP = '1.2.3.4' SERVER_PORT = 9001 # We don't want to establish a connection with a static port. Let the OS pick a random empty one. #MY_AS_CLIENT_PORT = 8510 TIMEOUT = 3 BUFFER_SIZE = 4096 def get_my_local_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # doesn't even have to be reachable s.connect(('10.255.255.255', 1)) IP = s.getsockname()[0] except Exception: IP = '127.0.0.1' finally: s.close() return bytes(IP, encoding='utf-8') def constantly_try_to_connect(sock): while True: try: sock.connect((SERVER_IP, SERVER_PORT)) except ConnectionRefusedError: print(f"Can't connect to the SERVER IP [{SERVER_IP}]:{SERVER_PORT} - does the server alive? Sleeping for a while...") time.sleep(1) except OSError: #print("Already connected to the server. Kill current session to reconnect...") pass def client(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) #sock.bind((get_my_local_ip().decode('utf-8'), MY_AS_CLIENT_PORT)) sock.settimeout(TIMEOUT) threading.Thread(target=constantly_try_to_connect, args=(sock,)).start() while True: try: packet = sock.recv(BUFFER_SIZE) if packet: print(packet) sock.sendall(get_my_local_ip()) except OSError: pass if __name__ == '__main__': client()  Now the current code results: ./tcphole_server.py We have a client. Client advertised his local IP as: 10.10.10.50 Although, our connection is from: [89.22.11.50]:32928 We have a client. Client advertised his local IP as: 192.168.1.20 Although, our connection is from: [78.88.77.66]:51928 ./tcphole_client1.py b'Greetings! This message came from SERVER as message back!' b'SERVER registered your data. Your local IP is: 192.168.1.20 You are connecting to the server FROM: 89.22.11.50:32928' ./tcphole_client2.py b'Greetings! This message came from SERVER as message back!' b'SERVER registered your data. Your local IP is: 10.10.10.50 You are connecting to the server FROM: 78.88.77.66:51928'  As you can see the server has all information to connect two clients. We can send details about the other peer individually through the current server-client connection. Now two questions remain in my head:  Assuming the SERVER sends information about CLIENT 1 and CLIENT 2 for each of the peers. And now the CLIENTS starts connecting like [89.22.11.50]:32928 <> [78.88.77.66]:51928 Does the SERVER should close the current connections with the CLIENTS?  How the CLIENT Router behaves? I assume it expecting the same EXTERNAL SERVER SRC IP [1.2.3.4], instead gets one of the CLIENTS EXT IP for instance [89.22.11.50] or [78.88.77.66]?   This is messier than I thought. Any help to move forward appreciated. Hope this would help other Devs/DevOps too.

Answer: Finally found the expected behavior! Don't want to give too much code here but I hope after this you will understand the basics of how to implement it. Best to have a separate file in each of the client's folder - nearby ./tcphole_client1.py and ./tcphole_client2.py. We need to connect fast after we initiated sessions with the SERVER. Now for instance: ./tcphole_client_connector1.py 32928 51928 ./tcphole_client_connector2.py 51928 32928  Remember? We need to connect to the same ports as we initiated with SERVER: [89.22.11.50]:32928 <> [78.88.77.66]:51928  The first port is needed to bind the socket (OUR). With the second port, we are trying to connect to the CLIENT. The other CLIENT doing the same procedure except it binds to his port and connects to yours bound port. If the ROUTER still has an active connection - SUCCESS.