⬆️ ⬇️

Writing your P2P messenger



Against the background of discussing the future of instant messengers and reading the article “Why Your Favorite Messenger Should Die,” I decided to share my experience in creating P2P applications for communication independently of third-party servers. More precisely, it’s just a stub that transfers one message from the client to the server, further expanding the functionality depends only on your imagination.



In this publication, we will write 3 simple P2P communication applications from anywhere in the world — a client, a server, and an alarm server.



We will need:

- one server with a white static IP address;

- 2 computers per NAT with connection type Full Cone NAT (or 1 computer with 2 virtual machines);

- STUN server.



Full Cone NAT is a type of network address translation in which there is a unique translation between the pairs “internal address: internal port” and “public address: public port”.

')

Here's what we can read about the STUN server on the Wiki :

“There are protocols that use UDP packets for voice, image, or text over IP networks. Unfortunately, if both parties are behind a NAT, the connection cannot be established in the usual way. This is where STUN turns out to be useful. It allows the client behind the address translation server (or behind several such servers) to determine its external IP address, the method of address and port translation on the external network associated with a specific internal port number. ”



The following python modules were used to solve the problem: socket, twisted, stun, sqlite3, os, sys.



For data exchange, both between the Server and the Client, and between the Server, the Client and the Signal Server - the UDP protocol is used.



In general terms, the functioning mechanism looks like this:



Server <-> STUN server

Client <-> STUN server



Server <-> Alarm Server

Client <-> Signal Server



Client -> Server



1. The client, being behind NAT with the connection type Full Cone NAT, sends a message to the STUN server, receives the response in the form of its external IP and open PORT;



2. The server, being behind NAT with the connection type Full Cone NAT, sends a message to the STUN server, receives the response in the form of its external IP and open PORT;



At the same time, the Client and the Server know the external (white) IP and PORT of the Signal Server;



3. The Server sends to the Signal Server data on its external IP and PORT, Signal Server stores them;



4. The client sends to the Signal Server data on its external IP and PORT and id_destination of the required Server for which its external IP, PORT, is waiting.



The Signal Server stores them, searches the database using id_destination and, in response, gives the found information as a string: 'id_host, name_host, ip_host, port_host';



5. The client accepts the found information, splits by delimiter and, using (ip_host, port_host), sends a message to the Server.



Applications are written in Python version 2.7, tested under Debian 7.7.



Create a server.py file with the contents:

server.py
# -*- coding: utf-8 -*- #SERVER from socket import * import sys import stun def sigserver_exch(): #  <->   #  <-  #  -         IP       IP  PORT.    . # IP  PORT  : v_sig_host = 'XX.XX.XX.XX' v_sig_port = XXXX #id  ,   , id   v_id_client = 'id_server_1002' v_name_client = 'name_server_2' v_id_server = 'none' #IP  PORT   v_ip_localhost = 'XX.XX.XX.XX' v_port_localhost = XXXX udp_socket = '' try: #   IP  PORT    STUN nat_type, external_ip, external_port = stun.get_ip_info() #   IP  PORT      host_sigserver = v_sig_host port_sigserver = v_sig_port addr_sigserv = (host_sigserver,port_sigserver) #       : # id +  +   IP  PORT, # id_dest -  'none' #  id      +  data_out = v_id_client + ',' + v_name_client + ',' + external_ip + ',' + str(external_port) + ',' + v_id_server #   : #    (AF_INET), #      udp_socket = socket(AF_INET, SOCK_DGRAM) #    IP   PORT    host = v_ip_localhost port = v_port_localhost addr = (host,port) #    IP  PORT udp_socket.bind(addr) print('socket binding') #     udp_socket.sendto(data_out,addr_sigserv) while True: #    - 'sigserv' (   ), #     # -   'Message from CLIENT!' data_in = udp_socket.recvfrom(1024) if data_in[0] == 'sigserv': print('signal server data: ', data_in) else: print('Message from CLIENT!') #  udp_socket.close() except: print('exit!') sys.exit(1) finally: if udp_socket <> '' udp_socket.close() sigserver_exch() 




Fill in the appropriate fields of the sections: "External IP and PORT SIGNAL SERVER" and "IP and PORT of this CUSTOMER".



Create a client.py file with the contents:

client.py
 # -*- coding: utf-8 -*- # CLIENT from socket import * import sys import stun def sigserver_exch(): #  <->   #  ->  #  -        IP #     IP  PORT   NAT    . # IP  PORT  : v_sig_host = 'XX.XX.XX.XX' v_sig_port = XXXX #id  ,   , id   v_id_client = 'id_client_1001' v_name_client = 'name_client_1' v_id_server = 'id_server_1002' #IP  PORT   v_ip_localhost = 'XX.XX.XX.XX' v_port_localhost = XXXX udp_socket = '' try: #   IP  PORT    STUN nat_type, external_ip, external_port = stun.get_ip_info() #   IP  PORT      host_sigserver = v_sig_host port_sigserver = v_sig_port addr_sigserv = (host_sigserver,port_sigserver) #       : # id +  +   IP  PORT, # id_dest - id      . #  id      +  data_out = v_id_client + ',' + v_name_client + ',' + external_ip + ',' + str(external_port) + ',' + v_id_server #   : #    (AF_INET), #      udp_socket = socket(AF_INET, SOCK_DGRAM) #    IP   PORT    host = v_ip_localhost port = v_port_localhost addr = (host,port) #    IP  PORT udp_socket.bind(addr) #     udp_socket.sendto(data_out, addr_sigserv) while True: #    - 'sigserv' (   ), #        #'Hello, SERVER!'       . data_in = udp_socket.recvfrom(1024) data_0 = data_in[0] data_p = data_0.split(",") if data_p[0] == 'sigserv': print('signal server data: ', data_p) udp_socket.sendto('Hello, SERVER!',(data_p[3],int(data_p[4]))) else: print("No, it is not Rio de Janeiro!") udp_socket.close() except: print ('Exit!') sys.exit(1) finally: if udp_socket <> '' udp_socket.close() sigserver_exch() 




Fill in the appropriate fields of the sections: "External IP and PORT SIGNAL SERVER" and "IP and PORT of this CUSTOMER".



Create a file signal_server.py with the contents:

signal_server.py
 # -*- coding: utf-8 -*- # SIGNAL SERVER #Twisted -  (event)  #   – event handler #        event handler #     reactor   twisted.internet from twisted.internet import reactor from twisted.internet.protocol import DatagramProtocol import sys, os import sqlite3 class Query_processing_server(DatagramProtocol): #   <->  #  ->  #  #   <->  #   -       #     IP  PORT # (  -   +   ) #   IP  PORT   . def datagramReceived(self, data, addr_out): conn = '' try: #     (,) [id_host,name_host,external_ip,external_port,id_dest] #id_dest -  id  data = data.split(",") #       sqlite3,         : path_to_db = raw_input('Enter name db. For example: "/home/user/new_db.db": ') path_to_db = os.path.join(path_to_db) #    conn = sqlite3.connect(path_to_db) #    conn.text_factory = str #   c = conn.cursor() #     c.execute('''CREATE TABLE IF NOT EXISTS compliance_table ("id_host" text UNIQUE, "name_host" text, "ip_host" text, \ "port_host" text)''') #  ,     #  ip, port    c.execute('INSERT OR IGNORE INTO compliance_table VALUES (?, ?, ?, ?);', data[0:len(data)-1]) #  conn.commit() c.execute('SELECT * FROM compliance_table') #      id c.execute('''SELECT id_host, name_host, ip_host, port_host from compliance_table WHERE id_host=?''', (data[len(data)-1],)) cf = c.fetchone() if cf == None: print ('Server_id not found!') else: #transport.write -    : id_host, name_host, ip_host, port_host   sigserver lst = 'sigserv' + ',' + cf[0] + ',' + cf[1] + ',' + cf[2] + ',' + cf[3] self.transport.write(str(lst), addr_out) #  conn.close() except: print ('Exit!') sys.exit(1) finally: if conn <> '' conn.close() reactor.listenUDP(9102, Query_processing_server()) print('reactor run!') reactor.run() 






Done!



The order of application launch is as follows:

- signal_server.py

- server.py

- client.py

Source: https://habr.com/ru/post/304150/



All Articles