Simple P2P-DEMO,UDP hole-punching

Source: Internet
Author: User
Tags ack sleep split trim

What is peer:

Peer-to-peer, simply put, is that two users can communicate directly with the network.


Why we need to peer:

1. Most network states are user A and User B communicate with each other, need an intermediary server to do the relay of the message. If users can communicate directly to users, then the server pressure can be mitigated.

2. Network latency is eliminated to a certain extent.

3. There are many real-world scenarios that require peer, such as VoIP.


Have a certain understanding of the network, you will know that the general user is not their own public IP, user and server communication, is the user actively send messages to the server (reverse connection) and open a server to let in the channel. The server knows the user's IP and can send a message to the user.


If a is known through the server, B's ip,a and B are not necessarily capable of communication. This involves the following:


NAT (network address translation) was proposed in 1994. When some hosts inside the private network have already been assigned local IP addresses (that is, private addresses that are used only in this private network), but now want to communicate with the host on the Internet (do not need encryption), you can use the NAT method.
This approach requires the installation of NAT software on a router that has a private network connected to the Internet. A router with NAT software is called a NAT router, and it has at least one valid external global IP address. In this way, all hosts that use local addresses will be able to connect to the Internet by translating their local addresses into global IP addresses on the NAT router when communicating with the outside world.
In addition, this way of representing more private IP addresses by using a small number of public IP addresses will help to slow down the exhaustion of the available IP address space. There is a description of NAT in RFC 1632.


And there are 3 of NAT:

Static conversion refers to the conversion of the private IP address of the internal network to a public IP address, the IP address pair is one-to-one, is immutable, a private IP address is only converted to a public IP address. With static transformations, external networks can access certain devices (such as servers) in the internal network.


Dynamic conversion means that when the private IP address of the internal network is converted to a public IP address, the IP address is indeterminate and is random, and all private IP addresses that are authorized to access the Internet can be randomly converted to any of the specified legitimate IP addresses. That is, you can convert dynamically whenever you specify which internal addresses can be converted, and which legal addresses are used as external addresses. Dynamic transformations can use multiple legitimate sets of external addresses. When the ISP provides a legitimate IP address that is slightly less than the number of computers inside the network. You can use the dynamic conversion method.


Ports multiplexing (port address Translation,pat) refers to changing the source port of an out-of-Office packet and making port conversions, that is, port addresses translation (Pat,port address translation). Use port multiplexing. All hosts on the internal network can share a legitimate external IP address to enable access to the Internet, thereby maximizing the savings in IP address resources. At the same time, can hide all the hosts inside the network, effectively avoid attacks from the Internet. Therefore, the most application in the network is the port multiplexing method.


Quote from (http://baike.baidu.com/link?url=b3s1JVUyBy_UucgNiCXLcMcVUHWJjbstQBjOJEoeaqWvKN_ tay-tsyvpom-asmkwajaindki1hq0eutswadk6_)


In other words, one of the NAT IP mapping methods is dynamic. (This NAT is difficult to get through)


For several other Nat that can be opened. We can build an intermediary server for the hole. This enables direct communication between the user and the user. The process is as follows:



1.A connect the server, the server gets the IP of a.

2.B connect the server, the server gets the IP of B.

3.A tells server A to try to send a message to B, and the server tells B to let B access the IP of a. At this point a channel B to a is opened.

4. The server tells a hole to complete, a to B to send a message.

5.B tells Server B to try to send a message to a, the server tells a, let a access the IP of B. This opens a channel for a to B.

6. The server tells B to make a hole and B sends a message to a.

This enables the interoperability of A and B messages.


The following is a simple demo, directly on the code:

Server:

Async_udp_echo_server.cpp//~~~~~~~~~~~~~~~~~~~~~~~~~////Copyright (c) 2003-2015 Christopher M. Kohlhoff (Chris At kohlhoff dot com)/////distributed under the Boost software License, Version 1.0. (see accompanying//file license_1_0.txt or copy at Http://www.boost.org/LICENSE_1_0.txt)//#include <cstdlib> #i Nclude <iostream> #include <sstream> #include <boost/asio.hpp> #include <boost/thread/thread.hpp

> Using BOOST::ASIO::IP::UDP; 
    Class Server {private:const std::string login = "Login"; 
    Const std::string login_sucess = "login_sucess";
 
    Const std::string login_error = "Login_error"; 
    Const std::string LOGOUT = "LOGOUT";
 
    Const std::string logout_error = "Logout_error"; 
    Const std::string geturl_p2s_request = "Geturlsreq";
 
    Const std::string geturl_s2p_response = "Geturlsrep"; 
    Const std::string p2pmsg = "p2pmsg"; 
    Const std::string p2smsg = "p2smsg";

    Const std::string s2pmsg = "S2pmgs";Const std::string translate_p2s_request = "P2stranslate";
    Const std::string translate_s2p_request = "S2ptranslate";

Const std::string ack_p2p = "ACK";
   
    Public:server (boost::asio::io_service& io_service, short port);
    void Userlogin (const std::string& username,const udp::endpoint& netpoint);
    void Userlogout (const std::string& userName);
    void Pasercommand (const std::string& cmd,const udp::endpoint& netpoint);
    void Usergeturl (const udp::endpoint& netpoint);
    void Usertransfer (const std::string& username,const udp::endpoint& netpoint);
    
    ~server ();
    void Do_receive ();

void Do_send (std::size_t length);
        Private:struct user{std::string userName;
        Udp::endpoint Netpoint;
            User (const user &user) {this->username = User.username;
        This->netpoint = User.netpoint; } User (std::string username,udp::endpoint netpoint): UserName (userName), netPoint (Netpoint) {}};
    Std::vector<user> clientlist;
    Udp::socket server;

    Udp::endpoint Remotepoint;
    enum {max_length = 1024};
    Char Data_[max_length];
int msglength;

 };


Async_udp_echo_server.cpp//~~~~~~~~~~~~~~~~~~~~~~~~~////Copyright (c) 2003-2015 Christopher M. Kohlhoff (Chris At kohlhoff dot com)/////distributed under the Boost software License, Version 1.0. (see accompanying//file license_1_0.txt or copy at Http://www.boost.org/LICENSE_1_0.txt)//#include "server.h" #includ E <cstdlib> #include <iostream> #include <sstream> #include <boost/asio.hpp> #include < stdarg.h> #include <boost/algorithm/string.hpp> #include <boost/tokenizer.hpp> #include <boost/
format.hpp> #include <stdlib.h> #include <sstream> using BOOST::ASIO::IP::UDP;

using namespace Std;
    String Merage (std::string string_array[],unsigned int len) {if (String_array = = NULL) return "";
    std::string msg = "";
        for (unsigned int i = 0;i < Len;++i) {if (i = = len-1) {msg + = String_array[i];
      } else {msg + = String_array[i];      msg + = "";
}} return msg; } void Server::d o_send (std::size_t length) {server.async_send_to (Boost::asio::buffer (data_, length), remote
            Point, [This] (boost::system::error_code,std::size_t) {do_receive ();
}
        ); } server::server (boost::asio::io_service& io_service,short Port): Server (Io_service,udp::endpoint (Udp::v4 (),

Port), Msglength ( -1) {do_receive ();}
        void Server::d o_receive () {Server.async_receive_from (Boost::asio::buffer (data_,max_length), Remotepoint, [This] (Boost::system::error_code ec,std::size_t BYTES_RECVD)
        {DATA_[BYTES_RECVD] = ' + ';
        cout<< "Do_recieve:" <<data_<<endl;
        
        Pasercommand (String (data_), remotepoint);
        if (!ec && bytes_recvd > 0) {do_send (msglength);
        } else {do_receive ();
}
    });

} server::~server () {}void Server::userlogin (const std::string& username,const udp::endpoint& netpoint) {clientlist.push_back (User
(Username,netpoint)); } void Server::usertransfer (const std::string& username,const udp::endpoint& netpoint) {cout<< "userNam
    E: "<<userName<<endl;
    int index =-1;
        for (unsigned int i = 0;i < Clientlist.size (); ++i) {if (Clientlist[i].username.compare (userName) = = 0)
            {index = i;
        Break
        }} if (Index! =-1) {StringStream stream;
        Stream<<remotepoint.port ();
        String String_port;
       stream>>string_port; Join ({}, "") std::string string_array[] = {translate_s2p_request, remotepoint.address
        (). To_string (), string_port};
        String msg = Merage (string_array,3);
        memcpy (Data_,msg.c_str (), msg.size ());   
        Data_[msg.size ()] = ' + '; Msglength = Msg.sizE () +1;
        cout<<string_array[1]<<endl;
        cout<<string_array[2]<< ":" <<string_port<<endl;
        cout<< "to" <<clientList[index].netPoint<<endl;
    Remotepoint = Clientlist[index].netpoint;
    } else {cout<< "no this user" <<endl; }} void Server::userlogout (const std::string& userName) {for (Auto it = Clientlist.begin (); It! = Clientlist.end (
        ) {it++) {if ((*it). Username.compare (userName) = = 0) {clientlist.erase (IT);
    }}} void Server::usergeturl (const udp::endpoint& netpoint) {std::string msg = geturl_s2p_response;
        for (unsigned int i = 0;i < Clientlist.size (); ++i) {StringStream stream;
        Stream<<clientlist[i].netpoint.port ();
        String String_port;
        stream>>string_port; String string_array[] = {msg, clientlist[i].username, clientlist[I].netpoint.address (). to_string (), string_port};
        msg = Merage (string_array,4);
        memcpy (Data_,msg.c_str (), msg.size ());
        Data_[msg.size ()] = ' + ';
    Msglength = Msg.size () +1;
    }} void Server::P asercommand (const std::string &cmd,const udp::endpoint& netpoint) {cout<<endl;
    cout<< "cmd:" <<cmd<<endl;
    Std::vector<std::string> vec_string;

    Boost::split (Vec_string,cmd,boost::is_any_of ("")); if (vec_string.size () > 0) {if (Vec_string[0].compare (LOGIN) = = 0) {userlogin (Vec_strin
        G[1],netpoint);
        } else if (Vec_string[0].compare (LOGOUT) = = 0) {userlogout (vec_string[1]);
        } else if (Vec_string[0].compare (geturl_p2s_request) = = 0) {usergeturl (netpoint); } else if (Vec_string[0].compare (translate_p2s_request) = = 0) {Usertransfer (VEC_STRING[2],NETP OinT);
        } else {cout<< "error command" <<endl;
 }
    }
}


//Async_udp_echo_server.cpp
//~~~~~~~~~~~~~~~~~~~~~~~~~
////
Copyright (c) 2003-2015 Christopher M. Kohlhoff (Chris at kohlhoff dot com)////
distributed under the Boost software License, Version 1. 0. (see accompanying
//file license_1_0.txt or copy at Http://www.boost.org/LICENSE_1_0.txt)
//

#include & lt;cstdlib>
#include <iostream>
#include <sstream>
#include <boost/asio.hpp>
#include "server.h"

using BOOST::ASIO::IP::UDP;

int main (int argc, char* argv[])
{
    try
    {
        boost::asio::io_service io_service;
        Server server (Io_service, 2280);
        Io_service.run ();
    }
    catch (std::exception& e)
    {
        Std::cerr << "Exception:" << e.what () << "\ n";
    }

    return 0;
}



Client: (C #)


Using System;
Using System.Collections.Generic;
Using System.Text;
Using System.Net;
Using System.Net.Sockets;


Using System.Threading; namespace-Peer.
        p2pclient {public class Client:idisposable {Private Const int maxretry = 10;
        Private UdpClient client;
        Private IPEndPoint Hostpoint;
        Private IPEndPoint Remotepoint; Private peer.
        Wellknown.usercollection userlist;
        private string MyName;
        private bool Receivedack;

        Private Thread Listenthread;
            Public Client (String serverip) {receivedack = false;
            Remotepoint = new IPEndPoint (ipaddress.any, 0); Hostpoint = new IPEndPoint (Ipaddress.parse (ServerIP), peer.
            WellKnown.P2PConsts.SRV_PORT);
            Client = new UdpClient (); userlist = new Peer.
            Wellknown.usercollection ();
        Listenthread = new Thread (new ThreadStart (Run)); } public void Start () {if (This.listenthreaD.threadstate = = threadstate.unstarted) {This.listenThread.Start ();
            }} public void Connecttoserver (string userName) {myName = UserName; String msg = Peer. WELLKNOWN.P2PCONSTS.MERAGECMD (peer.
            WellKnown.P2PConsts.LOGIN, UserName);
            byte[] ByteArray = System.Text.Encoding.Default.GetBytes (msg); Client.
            Send (ByteArray, Bytearray.length, Hostpoint);
        Requestgeturls (); } private bool Sendmessageto (string tousername, String message) {peer.
            Wellknown.user Touser = Userlist.find (tousername);
            if (Touser = = null) {return false; } for (int i = 0; i < maxretry; i++) {string p2pmsg = Peer. WELLKNOWN.P2PCONSTS.MERAGECMD (peer.
                Wellknown.p2pconsts.p2pmsg,message);
         byte[] P2pbytearray = System.Text.Encoding.Default.GetBytes (p2pmsg);       Client.
                Send (P2pbytearray,p2pbytearray.length,touser.netpoint); Wait for the receive thread to modify the markup for (int j = 0; J <; J + +) {if (this). Receivedack) {this.
                        Receivedack = false;
                    return true;
                    } else {Thread.Sleep (300); }}//udp Hole string msg = Peer. WELLKNOWN.P2PCONSTS.MERAGECMD (peer.
                Wellknown.p2pconsts.translate_p2s_request,myname,tousername);
                byte[] ByteArray = System.Text.Encoding.Default.GetBytes (msg); Client.
                Send (ByteArray, bytearray.length,hostpoint);
            Wait for the other party to send the message first thread.sleep (100);
        } return false; } private void Displayusers (peer. Wellknown.usercollection users) {foreach-Peer. Wellknown.uSer user in users) {Console.WriteLine ("Username: {0}, Ip:{1}, port:{2}", user.
            UserName, User.NetPoint.Address.ToString (), user.NetPoint.Port); }} private void Recieveuserlistmsg (string[] args) {if (args.
            Length < 4) return;
            Userlist.clear (); for (int i = 1; i < args. Length; i+=3) {Userlist.add (new peer). 
                        Wellknown.user (Args[i], new IPEndPoint (Ipaddress.parse (args[i+1]), Int.
            Parse (args[i+2]))); } this. 
        Displayusers (userlist); private void Recievetransfermsg (IPEndPoint remotepoint) {string msg = Peer. WELLKNOWN.P2PCONSTS.MERAGECMD (peer.
            WellKnown.P2PConsts.ACK_P2P);
            byte[] ByteArray = System.Text.Encoding.Default.GetBytes (msg); Client. Send (bytearray,bytearray.length, Remotepoint);
                for (int i = 1; i < one; i++) {Console.WriteLine (remotepoint.port + i); Client.
            Send (ByteArray, Bytearray.length, new IPEndPoint (Remotepoint.address,remotepoint.port + i)); }} private void Recievep2pmsg () {//reply to String msg = Peer. WELLKNOWN.P2PCONSTS.MERAGECMD (peer.
            WellKnown.P2PConsts.ACK_P2P);
            byte[] ByteArray = System.Text.Encoding.Default.GetBytes (msg); Client.
        Send (ByteArray, bytearray.length,remotepoint); private void Requestgeturls () {string msg = Peer. WELLKNOWN.P2PCONSTS.MERAGECMD (peer.
            WellKnown.P2PConsts.GETURL_P2S_REQUEST, MyName);
            byte[] ByteArray = System.Text.Encoding.Default.GetBytes (msg); Client.
        Send (ByteArray, Bytearray.length, Hostpoint); The private void Paserresponsecommand (String cmdstring) {cmdstring = cmdstring. TrIm (); string[] args = cmdstring.
            Split (new char[] {'});
            Console.WriteLine (cmdstring); if (args. Length > 0) {if (String.Compare (args[0), peer.
                WellKnown.P2PConsts.GETURL_S2P_RESPONSE, true) = = 0) {recieveuserlistmsg (args); } else if (String.Compare (args[0), peer. WellKnown.P2PConsts.TRANSLATE_S2P_REQUEST, true) = = 0) {recievetransfermsg (new IPENDP Oint (Ipaddress.parse (args[1]), Int.
                Parse (args[2])); } else if (String.Compare (args[0), peer.
                WellKnown.P2PConsts.P2PMSG, true) = = 0) {recievep2pmsg (); } else if (String.Compare (args[0), peer.
                    WellKnown.P2PConsts.ACK_P2P, true) = = 0) {Console.WriteLine ("Receive ACK"); This.
                Receivedack = true;
         }   }} private void Run () {byte[] buffer;
       
                while (true) {Console.WriteLine ("run!"); Buffer = client.
                Receive (ref remotepoint);
                String str = System.Text.Encoding.Default.GetString (buffer);
                Paserresponsecommand (str);
            Thread.Sleep (100); }} public void Pasercommand (string cmdstring) {cmdstring = cmdstring.
            Trim (); string[] args = cmdstring.

            Split (new char[] {'}); if (args. Length > 0) {if (String.Compare (args[0), peer. WellKnown.P2PConsts.LOGOUT, true) = = 0) {string msg = Peer. WELLKNOWN.P2PCONSTS.MERAGECMD (peer.
                    WellKnown.P2PConsts.LOGOUT, MyName);
                    byte[] ByteArray = System.Text.Encoding.Default.GetBytes (msg); Client.
  Send (ByteArray, Bytearray.length, Hostpoint);                  Dispose ();
                System.Environment.Exit (0); } else if (String.Compare (args[0), peer. WellKnown.P2PConsts.P2PMSG, true) = = 0) {if (args.
                        Length > 2) {string tousername = args[1];

                        String message = ""; for (int i = 2; i < args. Length;
                            i++) {if (args[i] = = "") Message + = "";
                        Else message + = Args[i]; } if (this.
                        Sendmessageto (tousername, message)) {Console.WriteLine ("Send ok!");
                    } else Console.WriteLine ("Send failed!"); }} else if (String.Compare (args[0), peer. WellKnown.P2PConsts.GETURL_P2S_REQUEST, true) = = 0) {requestgeturls ();
                } else {Console.WriteLine ("Unknown command {0}", cmdstring); }}} #region IDisposable member public void Dispose () {T
                ry {this.listenThread.Abort ();
            This.client.Close ();
 } catch {}} #endregion}}


Using System;
Using System.Collections.Generic;
Using System.Text;
Using System.Threading;
namespace-Peer. P2pclient
{
    class program
    {
        static void Main (string[] args)
        {
            Client client = new Client (" Xxx.xxx.xxx.xxx "); 
            Client. Connecttoserver ("xxx.xxx.xxx.xxx");

            Client. Start ();

            while (true)
            {
                string str = Console.ReadLine ();
                Client. Pasercommand (str);}}}



The server code is a simple modification to the demo in boost.


1. If you want to really implement a own turn server. Also need to consider the survival time of the hole, need to send some packets to maintain the existence of the hole.

2. Do not pass through the server to do the relay.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.