2012-02-04 15 views
18

Sto cercando di eseguire la perforazione UDP. Sto basando la mia teoria su this article e questo WIKI page, ma sto affrontando alcuni problemi con la codifica C# di esso. Ecco il mio problema:Implementazione perforatura UDP

Utilizzo del codice che è stato postato here Ora sono in grado di connettersi a un computer remoto e di ascoltare sulla stessa porta per le connessioni in ingresso (Collega 2 client UDP alla stessa porta).

Per qualche motivo i due collegamenti alla stessa porta si bloccano a vicenda dalla ricezione di dati. Ho un server UDP che risponde alla mia connessione, quindi se mi collego ad esso prima di legare qualsiasi altro client alla porta ottengo le sue risposte.

Se si associa un altro client alla porta, nessun dato verrà ricevuto su entrambi i client.

Di seguito sono 2 pezzi di codice che mostrano il mio problema. Il primo si connette a un server remoto per creare la regola sul dispositivo NAT e quindi un listener viene avviato su un thread diverso per acquisire i pacchetti in ingresso. Il codice quindi invia i pacchetti all'IP locale in modo che il listener possa ottenerlo. Il secondo invia solo pacchetti all'IP locale per assicurarsi che funzioni. So che questo non è il vero buco mentre sto inviando i pacchetti a me stesso senza vivere il dispositivo NAT. Sto affrontando un problema a questo punto e non immagino che questo sarà diverso se utilizzo un computer al di fuori del dispositivo NAT per connetterlo.

[EDIT] 2/4/2012 Ho provato ad usare un altro computer sulla mia rete e WireShark (sniffer di pacchetti) per testare l'ascoltatore. Vedo i pacchetti in arrivo dall'altro computer ma non vengono ricevuti dal client UDP del listener (udpServer) o dal client UDP del mittente (client).

[EDIT] 2/5/2010 Ho ora aggiunto una chiamata di funzione per chiudere il primo client UDP dopo l'invio e la ricezione iniziale dei pacchetti che vivono solo il secondo client UDP da ascoltare sulla porta. Funziona e posso ricevere pacchetti dalla rete su quella porta. Ora cercherò di inviare e ricevere pacchetti dall'esterno della rete. Pubblicherò le mie scoperte non appena avrò trovato qualcosa.

Utilizzando questo codice ottengo i dati sul client di ascolto:

static void Main(string[] args) 
{ 
    IPEndPoint localpt = new IPEndPoint(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545); 

    ThreadPool.QueueUserWorkItem(delegate 
    { 
     UdpClient udpServer = new UdpClient(); 
     udpServer.ExclusiveAddressUse = false; 
     udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
     udpServer.Client.Bind(localpt); 

     IPEndPoint inEndPoint = new IPEndPoint(IPAddress.Any, 0); 
     Console.WriteLine("Listening on " + localpt + "."); 
     byte[] buffer = udpServer.Receive(ref inEndPoint); //this line will block forever 
     Console.WriteLine("Receive from " + inEndPoint + " " + Encoding.ASCII.GetString(buffer) + "."); 
    }); 

    Thread.Sleep(1000); 

    UdpClient udpServer2 = new UdpClient(6000); 

    // the following lines work and the data is received 
    udpServer2.Connect(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545); 
    udpServer2.Send(new byte[] { 0x41 }, 1); 

    Console.Read(); 
} 

Se uso il seguente codice, dopo il trasferimento di collegamento e di dati tra il mio client e server, l'ascolto del cliente UDP non riceverà nulla:

static void Main(string[] args) 
{ 
    IPEndPoint localpt = new IPEndPoint(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545); 

    //if the following lines up until serverConnect(); are removed all packets are received correctly 
    client = new UdpClient(); 
    client.ExclusiveAddressUse = false; 
    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
    client.Client.Bind(localpt); 
    remoteServerConnect(); //connection to remote server is done here 
          //response is received correctly and printed to the console 

    ThreadPool.QueueUserWorkItem(delegate 
    { 
     UdpClient udpServer = new UdpClient(); 
     udpServer.ExclusiveAddressUse = false; 
     udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
     udpServer.Client.Bind(localpt); 

     IPEndPoint inEndPoint = new IPEndPoint(IPAddress.Any, 0); 
     Console.WriteLine("Listening on " + localpt + "."); 
     byte[] buffer = udpServer.Receive(ref inEndPoint); //this line will block forever 
     Console.WriteLine("Receive from " + inEndPoint + " " + Encoding.ASCII.GetString(buffer) + "."); 
    }); 

    Thread.Sleep(1000); 

    UdpClient udpServer2 = new UdpClient(6000); 

    // I expected the following line to work and to receive this as well 
    udpServer2.Connect(Dns.Resolve(Dns.GetHostName()).AddressList[0], 4545); 
    udpServer2.Send(new byte[] { 0x41 }, 1); 

    Console.Read(); 
} 
+0

Come gestire le situazioni in cui si perde il pacchetto relativo all'IP o alla porta? – user1914692

risposta

1

Aggiornamento:

Qualunque dei UdpClients lega prima è quella che verrà inviata pacchetti in arrivo da Windows. Nel tuo esempio prova a spostare il blocco di codice che imposta il thread in ascolto verso l'alto.

Sei sicuro che il problema non è solo che il thread di ricezione è scritto solo per gestire una singola ricezione? Prova a sostituire il thread di ricezione con come di seguito.

ThreadPool.QueueUserWorkItem(delegate 
{ 
    UdpClient udpServer = new UdpClient(); 
    udpServer.ExclusiveAddressUse = false; 
    udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
    udpServer.Client.Bind(localpt); 

    IPEndPoint inEndPoint = new IPEndPoint(IPAddress.Any, 0); 
    Console.WriteLine("Listening on " + localpt + "."); 

    while (inEndPoint != null) 
    { 
     byte[] buffer = udpServer.Receive(ref inEndPoint); 
     Console.WriteLine("Bytes received from " + inEndPoint + " " + Encoding.ASCII.GetString(buffer) + "."); 
    } 
}); 
+0

Non sto ricevendo il primo pacchetto. Il thread è bloccato in ricezione, mettendo la ricezione in un ciclo while non funzionerà. Inoltre, vedo il pacchetto arrivare al mio PC nello sniffer di pacchetti, non solo ai client. Hai un esempio funzionante di perforazione UDP? – brooc

+0

Ho appena provato con un punto di interruzione sulla linea di stampa e non lo raggiunge mai. – brooc

+0

In teoria tutto ciò che devi fare per creare un'associazione in un NAT è inviare un pacchetto da un socket privato a un socket pubblico. Successivamente il NAT dovrebbe passare tutti i pacchetti da quel socket pubblico al socket privato (almeno fino al timeout del bind che dovrebbe essere un minimo di X minuti). Sembra che sia quello che stai facendo quindi dovrebbe funzionare bene. – sipwiz

4

Hai provato a usare le funzioni asincrone, ecco un esempio di come si potrebbe farlo funzionare si potrebbe essere necessario un po 'di lavoro per renderlo funzionale al 100%:

public void HolePunch(String ServerIp, Int32 Port) 
    { 
     IPEndPoint LocalPt = new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], Port); 
     UdpClient Client = new UdpClient(); 
     Client.ExclusiveAddressUse = false; 
     Client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
     Client.Client.Bind(LocalPt); 

     IPEndPoint RemotePt = new IPEndPoint(IPAddress.Parse(ServerIp), Port); 

     // This Part Sends your local endpoint to the server so if the two peers are on the same nat they can bypass it, you can omit this if you wish to just use the remote endpoint. 
     byte[] IPBuffer = System.Text.Encoding.UTF8.GetBytes(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0].ToString()); 
     byte[] LengthBuffer = BitConverter.GetBytes(IPBuffer.Length); 
     byte[] PortBuffer = BitConverter.GetBytes(Port); 
     byte[] Buffer = new byte[IPBuffer.Length + LengthBuffer.Length + PortBuffer.Length]; 
     LengthBuffer.CopyTo(Buffer,0); 
     IPBuffer.CopyTo(Buffer, LengthBuffer.Length); 
     PortBuffer.CopyTo(Buffer, IPBuffer.Length + LengthBuffer.Length); 
     Client.BeginSend(Buffer, Buffer.Length, RemotePt, new AsyncCallback(SendCallback), Client); 

     // Wait to receve something 
     BeginReceive(Client, Port); 

     // you may want to use a auto or manual ResetEvent here and have the server send back a confirmation, the server should have now stored your local (you sent it) and remote endpoint. 

     // you now need to work out who you need to connect to then ask the server for there remote and local end point then need to try to connect to the local first then the remote. 
     // if the server knows who you need to connect to you could just have it send you the endpoints as the confirmation. 

     // you may also need to keep this open with a keepalive packet untill it is time to connect to the peer or peers. 

     // once you have the endpoints of the peer you can close this connection unless you need to keep asking the server for other endpoints 

     Client.Close(); 
    } 

    public void ConnectToPeer(String PeerIp, Int32 Port) 
    { 
     IPEndPoint LocalPt = new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], Port); 
     UdpClient Client = new UdpClient(); 
     Client.ExclusiveAddressUse = false; 
     Client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
     Client.Client.Bind(LocalPt); 
     IPEndPoint RemotePt = new IPEndPoint(IPAddress.Parse(PeerIp), Port); 
     Client.Connect(RemotePt); 
     //you may want to keep the peer client connections in a list. 

     BeginReceive(Client, Port); 
    } 

    public void SendCallback(IAsyncResult ar) 
    { 
     UdpClient Client = (UdpClient)ar.AsyncState; 
     Client.EndSend(ar); 
    } 

    public void BeginReceive(UdpClient Client, Int32 Port) 
    { 
     IPEndPoint ListenPt = new IPEndPoint(IPAddress.Any, Port); 

     Object[] State = new Object[] { Client, ListenPt }; 

     Client.BeginReceive(new AsyncCallback(ReceiveCallback), State); 
    } 

    public void ReceiveCallback(IAsyncResult ar) 
    { 
     UdpClient Client = (UdpClient)((Object[])ar.AsyncState)[0]; 
     IPEndPoint ListenPt = (IPEndPoint)((Object[])ar.AsyncState)[0]; 

     Byte[] receiveBytes = Client.EndReceive(ar, ref ListenPt); 
    } 

spero questo aiuta.

+0

dove mettere gli IP esterni sia quello del server quando configurato e quello di Peer quando si connettono al server? –

13

Se ho capito bene, stai provando a comunicare peer-to-peer tra 2 client ciascuno dietro un NAT diverso, utilizzando un server di mediazione per perforare?

Alcuni anni fa ho fatto la stessa identica cosa in C#, non ho ancora trovato il codice, ma mal darvi alcune indicazioni se ti piace:

In primo luogo, non vorrei usare la Connect() funzione su udpclient, dal momento che UDP è un protocollo senza connessione, tutta questa funzione è in realtà nascondere la funzionalità di un socket UDP.

Si dovrebbe perfrom le seguenti operazioni:

  1. aprire un socket UDP su un server con la sua porte non bloccato da un firewall, ad una porta specifica (ad esempio, Bind questa presa per un porto scelto per esempio 23000)
  2. Creare un socket UDP sul primo client e inviare qualcosa al server su 23000. Non collegare questo socket. Quando un udp viene utilizzato per inviare un pacchetto, Windows assegnerà automaticamente una porta libera alla presa
  3. Fai lo stesso dall'altro client
  4. Il server ha ora ricevuto 2 pacchetti da 2 client in 2 indirizzi diversi con 2 diversi porti. Verificare se il server può inviare pacchetti sullo stesso indirizzo e porta. (Se questo non funziona hai fatto qualcosa di sbagliato o il tuo NAT non funziona. Sai che funziona se puoi giocare senza aprire le porte: D)
  5. Il server dovrebbe ora inviare l'indirizzo e la porta degli altri client a ciascun cliente connesso.
  6. Un client dovrebbe ora essere in grado di inviare pacchetti utilizzando UDP agli indirizzi ricevuti dal server.

Si noti che la porta utilizzata sul nat non è probabilmente la stessa porta del PC client !! Il server dovrebbe distribuire questa porta esterna ai client. È necessario utilizzare gli indirizzi esterni e le porte esterne da inviare a!

Si noti inoltre che il NAT potrebbe non supportare questo tipo di port forwarding. Alcuni NAT inoltrano tutto il traffico in entrata su una porta assegnata al client, che è ciò che desideri. Ma alcuni nats filtrano sugli indirizzi dei pacchetti in entrata in modo da bloccare i pacchetti degli altri client. Ciò è improbabile se si utilizza un router utente personale standard.

+0

Grazie per la risposta! Farò un tentativo. Penso che potrei non aver provato a inviare pacchetti in entrambe le direzioni ... – brooc

+2

Ok, fammi sapere se riesci a farlo funzionare :) Un'altra cosa, ho capito correttamente che stai collegando più socket alla stessa porta? Nella maggior parte dei casi si dovrebbe usare solo un socket per porta, non sapevo che fosse possibile collegare più socket in ogni caso: D – MHGameWork

2

Modifica: dopo molte più prove questo non sembra funzionare per me a meno che non abiliti UPnP. Quindi molte delle cose che ho scritto qui potrebbero essere utili ma molte persone non hanno UPnP abilitato (perché è un rischio per la sicurezza) quindi non funzionerà per loro.

Ecco un codice che utilizza PubNub come server relay :). Non consiglio di usare questo codice senza test perché non è perfetto (non sono sicuro che sia sicuro o il modo giusto di fare le cose? Idk non sono un esperto di networking) ma dovrebbe darti un'idea di cosa fare. Almeno ha funzionato per me finora in un progetto di hobby. Le cose che manca sono:

  • Verificare se il client si trova sulla LAN. Mi limito ad inviare a entrambe le funzioni per la tua LAN e un dispositivo su un'altra rete, ma ciò è molto inefficiente.
  • Test quando il client interrompe l'ascolto se, ad esempio, ha chiuso il programma.Poiché questo è UDP, è senza stato quindi non importa se stiamo inviando messaggi nel vuoto ma probabilmente non dovremmo farlo se nessuno li sta ricevendo
  • Io uso Open.NAT per fare port forwarding programmaticamente ma questo potrebbe non funzionare su alcuni dispositivi. Nello specifico, utilizza UPnP, che è un po 'insicuro e richiede la porta UDP 1900 per il port forwarding manuale. Una volta eseguita questa operazione, è supportata dalla maggior parte dei router, ma molti non l'hanno ancora fatto.

Quindi, prima di tutto, è necessario un modo per ottenere i propri IP esterni e locali. Ecco il codice per ottenere il vostro IP locale:

// From http://stackoverflow.com/questions/6803073/get-local-ip-address 
public string GetLocalIp() 
{ 
    var host = Dns.GetHostEntry(Dns.GetHostName()); 
    foreach (var ip in host.AddressList) 
    { 
     if (ip.AddressFamily == AddressFamily.InterNetwork) 
     { 
      return ip.ToString(); 
     } 
    } 
    throw new Exception("Failed to get local IP"); 
} 

E qui è un po 'di codice per ottenere il vostro IP esterno tramite cercando alcuni siti web che sono stati progettati per restituire il tuo IP esterno

public string GetExternalIp() 
{ 
    for (int i = 0; i < 2; i++) 
    { 
     string res = GetExternalIpWithTimeout(400); 
     if (res != "") 
     { 
      return res; 
     } 
    } 
    throw new Exception("Failed to get external IP"); 
} 
private static string GetExternalIpWithTimeout(int timeoutMillis) 
{ 
    string[] sites = new string[] { 
     "http://ipinfo.io/ip", 
     "http://icanhazip.com/", 
     "http://ipof.in/txt", 
     "http://ifconfig.me/ip", 
     "http://ipecho.net/plain" 
    }; 
    foreach (string site in sites) 
    { 
     try 
     { 
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(site); 
      request.Timeout = timeoutMillis; 
      using (var webResponse = (HttpWebResponse)request.GetResponse()) 
      { 
       using (Stream responseStream = webResponse.GetResponseStream()) 
       { 
        using (StreamReader responseReader = new System.IO.StreamReader(responseStream, Encoding.UTF8)) 
        { 
         return responseReader.ReadToEnd().Trim(); 
        } 
       } 
      } 
     } 
     catch 
     { 
      continue; 
     } 
    } 

    return ""; 

} 

Ora abbiamo bisogno di trova una porta aperta e la inoltra a una porta esterna. Come accennato in precedenza ho usato Open.NAT. Per prima cosa, metti insieme un elenco di porte che ritieni siano ragionevoli per l'applicazione da utilizzare dopo aver consultato lo registered UDP ports. Qui ci sono alcuni, ad esempio:

public static int[] ports = new int[] 
{ 
    5283, 
    5284, 
    5285, 
    5286, 
    5287, 
    5288, 
    5289, 
    5290, 
    5291, 
    5292, 
    5293, 
    5294, 
    5295, 
    5296, 
    5297 
}; 

Ora possiamo ciclo attraverso di loro e, auspicabilmente, trovare uno che non è in uso per usare il port forwarding in:

public UdpClient GetUDPClientFromPorts(out Socket portHolder, out string localIp, out int localPort, out string externalIp, out int externalPort) 
{ 
    localIp = GetLocalIp(); 
    externalIp = GetExternalIp(); 

    var discoverer = new Open.Nat.NatDiscoverer(); 
    var device = discoverer.DiscoverDeviceAsync().Result; 

    IPAddress localAddr = IPAddress.Parse(localIp); 
    int workingPort = -1; 
    for (int i = 0; i < ports.Length; i++) 
    { 
     try 
     { 
      // You can alternatively test tcp with nc -vz externalip 5293 in linux and 
      // udp with nc -vz -u externalip 5293 in linux 
      Socket tempServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 
      tempServer.Bind(new IPEndPoint(localAddr, ports[i])); 
      tempServer.Close(); 
      workingPort = ports[i]; 
      break; 
     } 
     catch 
     { 
     // Binding failed, port is in use, try next one 
     } 
    } 


    if (workingPort == -1) 
    { 
     throw new Exception("Failed to connect to a port"); 
    } 


    int localPort = workingPort; 

    // You could try a different external port if the below code doesn't work 
    externalPort = workingPort; 

    // Mapping ports 
    device.CreatePortMapAsync(new Open.Nat.Mapping(Open.Nat.Protocol.Udp, localPort, externalPort)); 

    // Bind a socket to our port to "claim" it or cry if someone else is now using it 
    try 
    { 
     portHolder = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 
     portHolder.Bind(new IPEndPoint(localAddr, localPort)); 
    } 
    catch 
    { 
     throw new Exception("Failed, someone is now using local port: " + localPort); 
    } 


    // Make a UDP Client that will use that port 
    UdpClient udpClient = new UdpClient(localPort); 
    return udpClient; 
} 

Ora per il codice del server relay PubNub (P2PPeer sarà definito più avanti in seguito). C'è molto qui, quindi non sono realmente intenzione di spiegarlo ma si spera che il codice è abbastanza chiara per aiutarvi a capire che cosa sta succedendo

public delegate void NewPeerCallback(P2PPeer newPeer); 
public event NewPeerCallback OnNewPeerConnection; 

public Pubnub pubnub; 
public string pubnubChannelName; 
public string localIp; 
public string externalIp; 
public int localPort; 
public int externalPort; 
public UdpClient udpClient; 
HashSet<string> uniqueIdsPubNubSeen; 
object peerLock = new object(); 
Dictionary<string, P2PPeer> connectedPeers; 
string myPeerDataString; 

public void InitPubnub(string pubnubPublishKey, string pubnubSubscribeKey, string pubnubChannelName) 
{ 
    uniqueIdsPubNubSeen = new HashSet<string>(); 
    connectedPeers = new Dictionary<string, P2PPeer>; 
    pubnub = new Pubnub(pubnubPublishKey, pubnubSubscribeKey); 
    myPeerDataString = localIp + " " + externalIp + " " + localPort + " " + externalPort + " " + pubnub.SessionUUID; 
    this.pubnubChannelName = pubnubChannelName; 
    pubnub.Subscribe<string>(
     pubnubChannelName, 
     OnPubNubMessage, 
     OnPubNubConnect, 
     OnPubNubError); 
    return pubnub; 
} 

//// Subscribe callbacks 
void OnPubNubConnect(string res) 
{ 
    pubnub.Publish<string>(pubnubChannelName, connectionDataString, OnPubNubTheyGotMessage, OnPubNubMessageFailed); 
} 

void OnPubNubError(PubnubClientError clientError) 
{ 
    throw new Exception("PubNub error on subscribe: " + clientError.Message); 
} 

void OnPubNubMessage(string message) 
{ 
    // The message will be the string ["localIp externalIp localPort externalPort","messageId","channelName"] 
    string[] splitMessage = message.Trim().Substring(1, message.Length - 2).Split(new char[] { ',' }); 
    string peerDataString = splitMessage[0].Trim().Substring(1, splitMessage[0].Trim().Length - 2); 

    // If you want these, I don't need them 
    //string peerMessageId = splitMessage[1].Trim().Substring(1, splitMessage[1].Trim().Length - 2); 
    //string channelName = splitMessage[2].Trim().Substring(1, splitMessage[2].Trim().Length - 2); 


    string[] pieces = peerDataString.Split(new char[] { ' ', '\t' }); 
    string peerLocalIp = pieces[0].Trim(); 
    string peerExternalIp = pieces[1].Trim(); 
    string peerLocalPort = int.Parse(pieces[2].Trim()); 
    string peerExternalPort = int.Parse(pieces[3].Trim()); 
    string peerPubnubUniqueId = pieces[4].Trim(); 

    pubNubUniqueId = pieces[4].Trim(); 

    // If you are on the same device then you have to do this for it to work idk why 
    if (peerLocalIp == localIp && peerExternalIp == externalIp) 
    { 
     peerLocalIp = "127.0.0.1"; 
    } 


    // From me, ignore 
    if (peerPubnubUniqueId == pubnub.SessionUUID) 
    { 
     return; 
    } 

    // We haven't set up our connection yet, what are we doing 
    if (udpClient == null) 
    { 
     return; 
    } 


    // From someone else 


    IPEndPoint peerEndPoint = new IPEndPoint(IPAddress.Parse(peerExternalIp), peerExternalPort); 
    IPEndPoint peerEndPointLocal = new IPEndPoint(IPAddress.Parse(peerLocalIp), peerLocalPort); 

    // First time we have heard from them 
    if (!uniqueIdsPubNubSeen.Contains(peerPubnubUniqueId)) 
    { 
     uniqueIdsPubNubSeen.Add(peerPubnubUniqueId); 

     // Dummy messages to do UDP hole punching, these may or may not go through and that is fine 
     udpClient.Send(new byte[10], 10, peerEndPoint); 
     udpClient.Send(new byte[10], 10, peerEndPointLocal); // This is if they are on a LAN, we will try both 
     pubnub.Publish<string>(pubnubChannelName, myPeerDataString, OnPubNubTheyGotMessage, OnPubNubMessageFailed); 
    } 
    // Second time we have heard from them, after then we don't care because we are connected 
    else if (!connectedPeers.ContainsKey(peerPubnubUniqueId)) 
    { 
     //bool isOnLan = IsOnLan(IPAddress.Parse(peerExternalIp)); TODO, this would be nice to test for 
     bool isOnLan = false; // For now we will just do things for both 
     P2PPeer peer = new P2PPeer(peerLocalIp, peerExternalIp, peerLocalPort, peerExternalPort, this, isOnLan); 
     lock (peerLock) 
     { 
      connectedPeers.Add(peerPubnubUniqueId, peer); 
     } 

     // More dummy messages because why not 
     udpClient.Send(new byte[10], 10, peerEndPoint); 
     udpClient.Send(new byte[10], 10, peerEndPointLocal); 


     pubnub.Publish<string>(pubnubChannelName, connectionDataString, OnPubNubTheyGotMessage, OnPubNubMessageFailed); 
     if (OnNewPeerConnection != null) 
     { 
      OnNewPeerConnection(peer); 
     } 
    } 
} 

//// Publish callbacks 
void OnPubNubTheyGotMessage(object result) 
{ 

} 

void OnPubNubMessageFailed(PubnubClientError clientError) 
{ 
    throw new Exception("PubNub error on publish: " + clientError.Message); 
} 

Ed ecco un P2PPeer

public class P2PPeer 
{ 
    public string localIp; 
    public string externalIp; 
    public int localPort; 
    public int externalPort; 
    public bool isOnLan; 

    P2PClient client; 

    public delegate void ReceivedBytesFromPeerCallback(byte[] bytes); 

    public event ReceivedBytesFromPeerCallback OnReceivedBytesFromPeer; 


    public P2PPeer(string localIp, string externalIp, int localPort, int externalPort, P2PClient client, bool isOnLan) 
    { 
     this.localIp = localIp; 
     this.externalIp = externalIp; 
     this.localPort = localPort; 
     this.externalPort = externalPort; 
     this.client = client; 
     this.isOnLan = isOnLan; 



     if (isOnLan) 
     { 
      IPEndPoint endPointLocal = new IPEndPoint(IPAddress.Parse(localIp), localPort); 
      Thread localListener = new Thread(() => ReceiveMessage(endPointLocal)); 
      localListener.IsBackground = true; 
      localListener.Start(); 
     } 

     else 
     { 
      IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(externalIp), externalPort); 
      Thread externalListener = new Thread(() => ReceiveMessage(endPoint)); 
      externalListener.IsBackground = true; 
      externalListener.Start(); 
     } 
    } 

    public void SendBytes(byte[] data) 
    { 
     if (client.udpClient == null) 
     { 
      throw new Exception("P2PClient doesn't have a udpSocket open anymore"); 
     } 
     //if (isOnLan) // This would work but I'm not sure how to test if they are on LAN so I'll just use both for now 
     { 
      client.udpClient.Send(data, data.Length, new IPEndPoint(IPAddress.Parse(localIp), localPort)); 
     } 
     //else 
     { 
      client.udpClient.Send(data, data.Length, new IPEndPoint(IPAddress.Parse(externalIp), externalPort)); 
     } 
    } 

    // Encoded in UTF8 
    public void SendString(string str) 
    { 
     SendBytes(System.Text.Encoding.UTF8.GetBytes(str)); 
    } 


    void ReceiveMessage(IPEndPoint endPoint) 
    { 
     while (client.udpClient != null) 
     { 
      byte[] message = client.udpClient.Receive(ref endPoint); 
      if (OnReceivedBytesFromPeer != null) 
      { 
       OnReceivedBytesFromPeer(message); 
      } 
      //string receiveString = Encoding.UTF8.GetString(message); 
      //Console.Log("got: " + receiveString); 
     } 
    } 
} 

Infine, qui ci sono tutte le mie usings:

using PubNubMessaging.Core; // Get from PubNub GitHub for C#, I used the Unity3D library 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System.Threading; 

sono aperto a commenti e domande, non esitate a dare risposte se qualcosa qui è una cattiva pratica o non funziona. Alcuni errori sono stati introdotti nella traduzione dal mio codice che risolverò qui alla fine, ma questo dovrebbe almeno darti l'idea di cosa fare.