2014-11-01 7 views
6

Frage ist unten. Hier ist mein aktueller Testcode, der nicht erfolgreich war.Wie mache ich TCP-Lochung?

static void Main(string[] args) 
{ 
    if (args.Count() != 3) 
    { 
     Console.WriteLine("Bad args"); 
    } 
    var ep = new IPEndPoint(IPAddress.Parse(args[0]), int.Parse(args[1])); 
    var lp = new IPEndPoint(IPAddress.Any, int.Parse(args[2])); 

    var s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
    s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
    s.Bind(lp); 

    var c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
    c.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
    c.Bind(lp); 

    Task.Run(() => { try { c.Connect(ep); } catch { } }); 
    s.Listen(10); 
    var v = s.Accept(); 
    v.Close(); 
} 

Wie kann ich tun TCP Lochen? Ich teste mit einem Remote-Server. Ich lasse wget local_public_ip:port/test laufen. Ich habe mein Router-Setup für Port 80, so dass es keinen Locher braucht. Mein Code hat eine Verbindung. Jetzt versuche ich andere Ports und ich kann nicht genau herausfinden, wie man das Loch schlägt.

Was ich getan haben, ist (C# -Code)

var l = new TcpListener(8090); 
l.Start(); 
try { var o = new TcpClient(); o.Connect("myserverip", 123); } 
catch(Exception ex) {} 
var e = l.AcceptSocket(); 
Console.WriteLine(e.RemoteEndPoint.AddressFamily); 

Ich dachte, vielleicht ich einrichten müssen den lokalen Endpunkt auf der TCP-Verbindung aus.

TcpClient(new System.Net.IPEndPoint(new System.Net.IPAddress(bytearray), port)); 

Ich habe einen Fehler gemacht und bekam diese Ausnahme

The requested address is not valid in its context 

den Byte-Array zu 192,168,1,5 Fixing bis es richtig ausgehende Verbindungen zu machen scheint. Jetzt, da ich eine Out-Verbindung zu der Remote-IP mit meinem Abhör-Port habe, dachte ich, wget würde sich mit mir verbinden können. Es war nicht der Fall

Wie mache ich TCP-Lochung?

+0

Haben Sie den lokalen Port von "o" als 8090 eingerichtet? Sie sprechen darüber, aber der Code stimmt nicht überein. Bitte aktualisieren Sie den Code. Entfernen Sie alle nicht definierten Variablen wie Bytearray und Port. wget muss sich von Port 123 aus verbinden. Hast du es geschafft? Wenn Sie keinen anderen Client verwenden. – usr

+0

@usr: Ich schrieb 'wget http: // mylocalip: 8090/test' Der' TcpClient o' soll eine Dummy-Verbindung herstellen. Port ist = 8090.Ich dachte, wenn der lokale Port der ausgehenden Verbindung der lokale Port ist, den ich höre, dann, wenn die entfernte Verbindung versucht, sich mit dem gleichen Port zu verbinden, wird der Router es durchlassen. Vielleicht funktioniert es aber nicht auf meinem Router. ABER gibt es Software, die erfolgreich ein Loch in meinem Router lochen. –

+0

Das sind neue Informationen, aber meine Fragen sind immer noch teilweise unbeantwortet: Haben Sie den lokalen Port von o zum 8090 eingerichtet? Sie sprechen darüber, aber der Code stimmt nicht überein. Bitte aktualisieren Sie den Code. Wenn Sie mit "o" anrufen, aktivieren Sie das Tuple (your-ip, your-port, remote-ip, remote-port) für die Kommunikation. Dies bedeutet, dass jede weitere Kommunikation diese Werte verwenden muss. wget muss Port 123 verwenden. o muss Port 8090 verwenden. Für den Fall, dass Sie nicht wissen: Ausgehende Verbindungen können auch den lokalen Port steuern. Das ist nur selten. – usr

Antwort

5

Ich würde die "sequentielle Lochungstechnik" verwenden, die in http://www.bford.info/pub/net/p2pnat/index.html ausführlich beschrieben wird. Es scheint viel einfacher zu sein, simultane Verbindungen und Socket-Wiederverwendung zu machen. Es ist nicht notwendig, dass die Lochung nichts genau tut gleichzeitig (das ist in verteilten Systemen sowieso ein bedeutungsloser Begriff).

Ich habe Lochung implementiert. Mein Router scheint es nicht zu mögen. Wireshark zeigt das ausgehende Loch, das SYN stimmt, aber die entfernte Partei kann nicht zu mir durchkommen. Ich überprüfe alle Ports mit TcpView.exe und deaktiviere alle Firewalls. Muss ein Routerproblem sein. (Es ist ein seltsamer und invasiver Router.)

class HolePunchingTest 
{ 
    IPEndPoint localEndPoint; 
    IPEndPoint remoteEndPoint; 
    bool useParallelAlgorithm; 

    public static void Run() 
    { 
     var ipHostEntry = Dns.GetHostEntry("REMOTE_HOST"); 

     new HolePunchingTest() 
     { 
      localEndPoint = new IPEndPoint(IPAddress.Parse("LOCAL_IP"), 1234), 
      remoteEndPoint = new IPEndPoint(ipHostEntry.AddressList.First().Address, 1235), 
      useParallelAlgorithm = true, 
     }.RunImpl(); 
    } 

    void RunImpl() 
    { 
     if (useParallelAlgorithm) 
     { 
      Parallel.Invoke(() => 
      { 
       while (true) 
       { 
        PunchHole(); 
       } 
      }, 
      () => RunServer()); 
     } 
     else 
     { 

      PunchHole(); 

      RunServer(); 
     } 
    } 

    void PunchHole() 
    { 
     Console.WriteLine("Punching hole..."); 

     using (var punchSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) 
     { 
      EnableReuseAddress(punchSocket); 

      punchSocket.Bind(localEndPoint); 
      try 
      { 
       punchSocket.Connect(remoteEndPoint); 
       Debug.Assert(false); 
      } 
      catch (SocketException socketException) 
      { 
       Console.WriteLine("Punching hole: " + socketException.SocketErrorCode); 
       Debug.Assert(socketException.SocketErrorCode == SocketError.TimedOut || socketException.SocketErrorCode == SocketError.ConnectionRefused); 
      } 
     } 

     Console.WriteLine("Hole punch completed."); 
    } 

    void RunServer() 
    { 
     using (var listeningSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) 
     { 
      EnableReuseAddress(listeningSocket); 

      listeningSocket.Bind(localEndPoint); 
      listeningSocket.Listen(0); 

      while (true) 
      { 
       var connectionSocket = listeningSocket.Accept(); 
       Task.Run(() => ProcessConnection(connectionSocket)); 
      } 
     } 
    } 

    void ProcessConnection(Socket connectionSocket) 
    { 
     Console.WriteLine("Socket accepted."); 

     using (connectionSocket) 
     { 
      connectionSocket.Shutdown(SocketShutdown.Both); 
     } 

     Console.WriteLine("Socket shut down."); 
    } 

    void EnableReuseAddress(Socket socket) 
    { 
     if (useParallelAlgorithm) 
      socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 
    } 
} 

Sie beiden Werte für useParallelAlgorithm versuchen. Beides sollte funktionieren.

Dieser Code ist für den Server. Es schlägt ein Loch in das lokale NAT. Sie können dann von der Remoteseite aus eine Verbindung mit einem beliebigen Client herstellen, der es ermöglicht, den lokalen Port auszuwählen. Ich habe curl.exe verwendet. Offensichtlich unterstützt Telnet unter Windows keine Bindung an einen Port. wget anscheinend weder.

Überprüfen Sie, ob die Ports auf beiden Seiten mit TcpView oder Process Explorer korrekt sind. Sie können Wireshark verwenden, um Pakete zu überprüfen. Stellen Sie einen Filter wie tcp.port = 1234 ein.

Wenn Sie "ausrufen", um ein Loch zu schlagen, aktivieren Sie das Tuple (your-ip, your-port, remote-ip, remote-port) für die Kommunikation. Dies bedeutet, dass jede weitere Kommunikation diese Werte verwenden muss. Alle Sockets (eingehende oder ausgehende) müssen diese genauen Portnummern verwenden. Falls Ihnen nicht bekannt ist: Ausgehende Verbindungen können auch den lokalen Port steuern. Das ist nur selten.

+0

Dies scheint zu funktionieren. Ich habe es mit 'useParallelAlgorithm = false' getestet. Ich wartete darauf, dass das Loch gelocht wurde, bevor ich es auf meinem Remote-Server laufen ließ. Es klappt. Es scheint, ein Loch durch iptables sowie –

+2

@ acidzombie24 groß zu schlagen, gut zu wissen! Schließlich gibt es einen sauberen Lochstanzcode im Web. Der gesamte Code, den ich gefunden habe, war schrecklich. – usr

+0

Hier ist eine saubere [Hallo Welt wie Locher, die Sie mögen] (https://gist.github.com/anonymous/03a3eaafd6839236dcc4). Es verwendet nur TcpClient und TcpListener. Keine Wiederverwendungsadresse oder rohe Sockets. Sind Sie sicher, dass Ihr Router den Locher nicht unterstützt/unterstützt? Wenn Sie eine Remote-Linux-Box zum Testen haben, kann ich Ihnen einige IPTable-Regeln übergeben, um den Block –