This is the code in serverside:
#!/usr/bin/env python
import sys
import logging
import socket
import struct
import fcntl
import os
from util import *
logger = logging.getLogger()
clients = {}
def main(host='**client public ip**', port=3443):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(1)
s.settimeout(30)
while True:
try:
conn, addr = s.accept()
except socket.timeout:
continue
logger.info('connection address: %s', addr)
data = recv_msg(conn)
priv_addr = msg_to_addr(data)
send_msg(conn, addr_to_msg(addr))
data = recv_msg(conn)
data_addr = msg_to_addr(data)
if data_addr == addr:
logger.info('client reply matches')
clients[addr] = Client(conn, addr, priv_addr)
else:
logger.info('client reply did not match')
conn.close()
logger.info('server - received data: %s', data)
if len(clients) == 2:
(addr1, c1), (addr2, c2) = clients.items()
logger.info('server - send client info to: %s', c1.pub)
send_msg(c1.conn, c2.peer_msg())
logger.info('server - send client info to: %s', c2.pub)
send_msg(c2.conn, c1.peer_msg())
clients.pop(addr1)
clients.pop(addr2)
conn.close()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
main(*addr_from_args(sys.argv))
and this is clientside:
#!/usr/bin/env python
import sys
import logging
import socket
import struct
from threading import Event, Thread
from util import *
logger = logging.getLogger('client')
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
STOP = Event()
def accept(port):
logger.info("accept %s", port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.bind(('**server's public ip**', port))
s.listen(1)
s.settimeout(5)
while not STOP.is_set():
try:
conn, addr = s.accept()
except socket.timeout:
continue
else:
logger.info("Accept %s connected!", port)
# STOP.set()
def connect(local_addr, addr):
logger.info("connect from %s to %s", local_addr, addr)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.bind(local_addr)
while not STOP.is_set():
try:
s.connect(addr)
except socket.error:
continue
# except Exception as exc:
# logger.exception("unexpected exception encountered")
# break
else:
logger.info("connected from %s to %s success!", local_addr, addr)
# STOP.set()
def main(host='**client's local ip**', port=3443):
sa = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sa.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sa.connect((host, port))
priv_addr = sa.getsockname()
send_msg(sa, addr_to_msg(priv_addr))
data = recv_msg(sa)
logger.info("client %s %s - received data: %s", priv_addr[0], priv_addr[1], data)
pub_addr = msg_to_addr(data)
send_msg(sa, addr_to_msg(pub_addr))
data = recv_msg(sa)
pubdata, privdata = data.split(b'|')
client_pub_addr = msg_to_addr(pubdata)
client_priv_addr = msg_to_addr(privdata)
logger.info(
"client public is %s and private is %s, peer public is %s private is %s",
pub_addr, priv_addr, client_pub_addr, client_priv_addr,
)
threads = {
'0_accept': Thread(target=accept, args=(priv_addr[1],)),
'1_accept': Thread(target=accept, args=(client_pub_addr[1],)),
'2_connect': Thread(target=connect, args=(priv_addr, client_pub_addr,)),
'3_connect': Thread(target=connect, args=(priv_addr, client_priv_addr,)),
}
for name in sorted(threads.keys()):
logger.info('start thread %s', name)
threads[name].start()
while threads:
keys = list(threads.keys())
for name in keys:
try:
threads[name].join(1)
except TimeoutError:
continue
if not threads[name].is_alive():
threads.pop(name)
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, message='%(asctime)s %(message)s')
main(*addr_from_args(sys.argv))
and this is the util.py code:
import struct
from collections import namedtuple
def addr_from_args(args, host='**server's local ip**', port=9999):
if len(args) >= 3:
host, port = args[1], int(args[2])
elif len(args) == 2:
host, port = host, int(args[1])
else:
host, port = host, port
return host, port
def msg_to_addr(data):
ip, port = data.decode('utf-8').strip().split(':')
return (ip, int(port))
def addr_to_msg(addr):
return '{}:{}'.format(addr[0], str(addr[1])).encode('utf-8')
def send_msg(sock, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
def recvall(sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = b''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
def recv_msg(sock):
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
class Client(namedtuple('Client', 'conn, pub, priv')):
def peer_msg(self):
return addr_to_msg(self.pub) + b'|' + addr_to_msg(self.priv)
and this is the error:
Traceback (most recent call last):
File "tcpclient.py", line 96, in <module>
main(*addr_from_args(sys.argv))
File "tcpclient.py", line 55, in main
sa.connect((host, port))
ConnectionRefusedError: [Errno 111] Connection refused
It's working in same network but in different one not!! For adding more details I would say that we have one client and on server that wanna communicate to each other, client and server are both behind the nat and the server has webserver and client want to go to the webserver but because of the nat problem it can't. so we are trying to make a hole punching peer to peer connection to make client able to see the webserver. we can't take any kind of ip static or commercial web service or isp or what ever that everyone say. what's wrong with this code?
in util.py the ip should be public not private
import struct
from collections import namedtuple
def addr_from_args(args, host='**server's ip public**', port=9999):
if len(args) >= 3:
host, port = args[1], int(args[2])
elif len(args) == 2:
host, port = host, int(args[1])
else:
host, port = host, port
return host, port
def msg_to_addr(data):
ip, port = data.decode('utf-8').strip().split(':')
return (ip, int(port))
def addr_to_msg(addr):
return '{}:{}'.format(addr[0], str(addr[1])).encode('utf-8')
def send_msg(sock, msg):
# Prefix each message with a 4-byte length (network byte order)
msg = struct.pack('>I', len(msg)) + msg
sock.sendall(msg)
def recvall(sock, n):
# Helper function to recv n bytes or return None if EOF is hit
data = b''
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
def recv_msg(sock):
# Read message length and unpack it into an integer
raw_msglen = recvall(sock, 4)
if not raw_msglen:
return None
msglen = struct.unpack('>I', raw_msglen)[0]
# Read the message data
return recvall(sock, msglen)
class Client(namedtuple('Client', 'conn, pub, priv')):
def peer_msg(self):
return addr_to_msg(self.pub) + b'|' + addr_to_msg(self.priv)