[SOLVED] Hole punching - Step by Step - with simple example lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- Key-Real · 2023-05-27 08:28 I managed to setup the first Step: I have a UDP server which accepts a client connection and now I knew the IP and Port of the clients. What is the second Step? ---------------------------------------------------------------------- nummer8 · 2023-05-28 10:04 In this thread hole punching is part of the discussion https://forum.lazarus.freepascal.org/index.php/topic,52110.0.html ---------------------------------------------------------------------- Key-Real · 2023-05-29 18:54 I'm on the last step: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  {$MODE OBJFPC}uses  cthreads, Classes, blcksock, sockets, Synautil, SysUtils, nettools; var      soket : TUDPBlockSocket;     clientNumer : string;     buf : TcharArray;    s : string;     bytesReceived : longint;     remoteIP, remotePort : string;  Type     TreadThread = class(TThread)    protected       procedure Execute; override;     public       soket : TUDPBlockSocket;      Constructor Create(CreateSuspended : boolean);    end;     TwriteThread = class(TThread)    protected       procedure Execute; override;     public       soket : TUDPBlockSocket;      Constructor Create(CreateSuspended : boolean);    end;  constructor TreadThread.Create(CreateSuspended : boolean);begin    inherited Create(CreateSuspended);    FreeOnTerminate := True;end; procedure TreadThread.Execute;var buf : TcharArray;begin    while (not Terminated) do begin      bytesReceived:= soket.recvBuffer(PChar(@buf), sizeof(TcharArray));      checkError(soket);      writeln(buf);      sleep(100);    end;end;    constructor TwriteThread.Create(CreateSuspended : boolean);begin    inherited Create(CreateSuspended);    FreeOnTerminate := True;end; procedure TwriteThread.Execute;var buf : TcharArray;    s : String;begin    while (not Terminated) do begin    s:= '->' + clientNumer;      buf:= s;      soket.SendBuffer(PChar(@buf[0]), length(s));      checkError(soket, 'write');      sleep(100);    end;end;       var    readThread : TreadThread;    writeThread : TwriteThread;     begin   soket:= TUDPBlockSocket.Create;  soket.bind('0.0.0.0', '9090');  soket.SetRemoteSin(remoteServerIP, '1500');   checkError(soket);     clientNumer:= '1';  if paramcount = 1 then clientNumer:= paramstr(1);      buf[0]:= char(strtoint(clientNumer));  soket.SendBuffer(PChar(@buf[0]), 1);   sleep(10);   soket.Free;     soket := TUDPBlockSocket.Create;  soket.CreateSocket;  soket.bind('0.0.0.0', '9090');  checkError(soket);   while True do begin    bytesReceived:= soket.recvBuffer(PChar(@buf), sizeof(buf));     if buf[0] = #3 then begin      writeln('Handshake!');      break;  end;   end;   soket.CloseSocket;  soket.Free;    s:=transcriptBuf2String(buf, 2, bytesReceived - 1);   s:=copy(s, pos('@', s) + 1, length(s));  remoteIP:=copy(s, 0, pos(':', s) - 1);  remotePort:=copy(s, pos(':', s) + 1, length(s));   writeln('other clinet is @', remoteIP, ':', remotePort, '!'); ////      At this point I have correct IP and Port of the other Client!!!!!!!//// ////  now I'm trying to connect and read/write... nothing happens :(     why?//   Soket:= TUDPBlockSocket.Create;  Soket.CreateSocket;  Soket.bind('0.0.0.0', '9090'); checkError(Soket, 'bind');  Soket.Connect(remoteIP, remotePort); checkError(Soket, 'connect');   readThread:=TreadThread.Create(false);  readThread.soket:=soket;  readThread.Start;   writeThread:= TwriteThread.Create(false);  writeThread.soket:=soket;  writeThread.Start;    readln;   Soket.CloseSocket;  Soket.Free;end.   I think I'm connecting wrong. How to establish connection? ---------------------------------------------------------------------- Key-Real · 2023-05-30 08:24 don't think I haven't look at the discussion at https://forum.lazarus.freepascal.org/index.php/topic,52110.45.html they tell just do a socket.connect(remoteIP, remotePort); this doesn't work pls help ---------------------------------------------------------------------- Warfley · 2023-05-30 11:57 The important part about hole punching is that you need a two way handshake. Basically when you punch the initial hole, the router registers it with a target address, so when then a package comes for that port, that is not from that target address, it will be ignored by the router. So what you need to do is after punching the first hole, is to then punch the same hole for the new target. This could look like this: Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i [Alice]           [Server]             [Bob]   |                    |                |   +--register(alice)-->|                  |   |                  |                  |   |                    |<--request(alice)-+   |                    |                  |   |<-----addr(Bob)-----+---addr(alice)--->|   |                                     |   +------------------punch--------------->|   |<-----------------punch----------------+   |                                     |   |<--------------Communicate------------>| The "punch" here is just an UDP message whose contents does not matter (it may or may not be blocked by the nat, depending if the punch of the other party has been successful first). Communication is only possible after both punches went through their respective nat. It's important that for all operations you always use the same private network port. So you always bind your socket to a fixed port, this way it's ensured that you can reuse the same output port. ---------------------------------------------------------------------- Key-Real · 2023-05-30 12:11 ok, at the first step I created a server on witch I register Alice IP+Port (line 3) why on Your Diagram in line 5 You write "request (Alice)? Should I not get the IP+Port of Bob like in step1? I would have bough addresses, so I can do line 7. How would line 9 looks in code? ---------------------------------------------------------------------- Warfley · 2023-05-30 12:28 In this scenario Alice is a kind of server waiting for a connection request. Bob then wants to connect to alice. The important part is that alice needs the address (IP + Port) of Bob and Bob needs the address (IP + Port) from alice. Then both parties must send at least one punching message, which will probably be dropped by the other parties NAT. After these two messages they can communicate. This may look like this (pseudo code, because I don't know how synapse works from the top of my head): Alice: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i socket.bind('0.0.0.0', LocalPort);repeat  socket.sendTo('register: alice', ServerAddr);  response := socket.recvFrom(ServerAddr, Timeout);until not response.DidTimeout; // Until we get the address from the server BobAddr := response.Contents; // get the address of bob that the server send us repeat  socket.sendTo('connect: alice', BobAddr); // functions as hole punch method  BobMessage := socket.recvFrom(BobAddr, Timeout);until not BobMessage.DidTimeout; // We now have received a message from bob, so we know that he can receive messages from us Bob: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i socket.bind('0.0.0.0', LocalPort);repeat  socket.sendTo('request: alice from bob', ServerAddr);  response := socket.recvFrom(ServerAddr, Timeout);until not response.DidTimeout; // Until we get the address from the server AliceAddr := response.Contents; // get the address of bob that the server send us repeat  socket.sendTo('connect: bob', BobAddr); // functions as hole punch method  AliceMessage := socket.recvFrom(BobAddr, Timeout);until not AliceMessage.DidTimeout; // We now have received a message from alice, so we know that he can receive messages from us Server: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i socket.bind('0.0.0.0', LocalPort);repeat  Message := socket.recvFrom(Any);  if Message.isRegister then    RegisteredClients[Message.ClientID] := Message.Addr  else if Message.IsRequest then  begin    TargetAddr := RegisteredClients[Message.TargetClientID];    socket.sendTo('addr: ' + Message.ClientID + Message.Addr, TargetAddr); // send Bobs addr to alice    socket.sendTo('addr: ' + Message.TargetClientID + TargetAddr, Message.Addr); // send Alices addr to bob  end;until Stopped; ---------------------------------------------------------------------- ginoo · 2023-05-31 06:25 HI, I think by the end of next month I will try to implement it too with the TCP protocol. I've never done such an implementation even though I needed it. If I find difficulties I will contact you at this post. Thanks experts. ---------------------------------------------------------------------- Warfley · 2023-05-31 07:46 Because TCP is a connection based protocol, many routers track the connection state and only allow for messages of that connection. This means you can't just holepunch your way through. At least not reliable through most routers So what you should do is do UDP hole punching and build a connection based protocol on top of it (like QUIC) ---------------------------------------------------------------------- Key-Real · 2023-05-31 08:27 SERVER: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODE OBJFPC}{$H+}uses  cthreads, Classes, blcksock, sockets, Synautil, SysUtils, HTTPSend, synaip, Process, synamisc, nettools; type    TclientInfo = record                    ip:string;                    port:string;                  end;var    soket : TUDPBlockSocket;                               clientA, clientB : TclientInfo;    s : string; procedure receive;begin    repeat        if soket.CanRead(3000) then begin            soket.RecvPacket (3000);          checkError(soket);            break;        end;        until false;    sleep(100);end;  begin    soket:= TUDPBlockSocket.Create;  soket.bind('0.0.0.0', '1500');    checkError(soket);      writeln('Waiting for A');    receive;    clientA.ip:= soket.GetRemoteSinIP;    clientA.port:= inttostr(soket.GetRemoteSinPort);    writeln('Client A at ', clientA.ip, ':', clientA.port);             writeln('Waiting for B');    receive;    clientB.ip:= soket.GetRemoteSinIP;    clientB.port:= inttostr(soket.GetRemoteSinPort);    writeln('Client B at ', clientB.ip, ':', clientB.port);          writeln('send to clientA @', clientA.ip, ':', clientA.port);    soket.connect(clientA.ip, clientA.port);    s:=clientB.ip + ':' + clientB.port;    soket.SendString(s);         writeln('send to clientB @', clientB.ip, ':', clientB.port);    soket.connect(clientB.ip, clientB.port);    s:=clientA.ip + ':' + clientA.port;    soket.SendString(s);       soket.CloseSocket;    soket.Free;end.  CLIENT: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODE OBJFPC}uses  cthreads, Classes, blcksock, sockets, Synautil, SysUtils, nettools; var     soket : TUDPBlockSocket;     clientNumber : string;    s : string;     remoteIP, remotePort : string;begin  soket:= TUDPBlockSocket.Create;  soket.bind('0.0.0.0', '9090');  soket.connect('178.254.3.95', '1500');  checkError(soket);     clientNumber:= '1';  if paramcount = 1 then clientNumber:= paramstr(1);        // start with parameter    '2' for Client B   soket.SendString(clientNumber);   repeat    if soket.CanRead(3000) then begin      s:= soket.RecvPacket (30000);      break;    end;    sleep(100);  until false;   remoteIP:= copy(s, 1, pos(':',s) - 1);  remotePort:= copy(s, pos(':',s) + 1, length(s));   writeln('punching ', remoteIP, ':', remotePort); ////// it works till here////   soket.connect(remoteIP, remotePort);  checkError(soket);  repeat    soket.SendString('connect from ' + clientNumber);    if soket.CanRead(3000) then begin      s:= soket.recvPacket(3000);      writeln(s);      break;    end;  until false;   writeln('connected');   Soket.CloseSocket;  Soket.Free;end.  I translated Your pseudo code to synapse. I'm getting the addresses of the client the right way. Can't punch the hole Can You pls correct the code? ---------------------------------------------------------------------- ginoo · 2023-05-31 14:25 Hello @Key-Real, unfortunately now I don't have time to try, later in a few weeks I'll try too. It is not certain that it will succeed, but we hope ... ---------------------------------------------------------------------- Key-Real · 2023-05-31 22:19 @Warfley: ok as I understand You wanna this: (one way punch first) ALLICE: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODE OBJFPC}uses    blcksock, sockets, SysUtils, nettools;var     soket : TUDPBlockSocket;    s : string;    remoteIP, remotePort : string;begin     writeln('Send to Server...');    soket:= TUDPBlockSocket.Create;    soket.bind('0.0.0.0', '9090');    soket.connect('178.254.3.95', '1500');    checkError(soket);    soket.SendString('Allice');    soket.Free;       writeln('Receive from Server...');    soket:= TUDPBlockSocket.Create;    soket.bind('0.0.0.0', '9090');    repeat      if soket.CanRead(3000) then begin        s:= soket.RecvPacket (3000);        break;      end;      sleep(100);    until false;    remoteIP:= copy(s, 1, pos(':', s) - 1);    remotePort:= copy(s, pos(':', s) + 1, length(s));    soket.Free;       writeln('Punching ', remoteIP, ':', remotePort, '...');    soket:= TUDPBlockSocket.Create;    soket.bind('0.0.0.0', '9090');    repeat      soket.connect(remoteIP, remotePort);      checkError(soket);       soket.SendString('from Allice');      sleep(100);    until false;     Soket.Free;end.  BOB: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODE OBJFPC}uses    blcksock, sockets, sysutils, nettools; var    soket : TUDPBlockSocket;     s : string;begin     writeln('Send to Server...');    soket:= TUDPBlockSocket.Create;    soket.bind('0.0.0.0', '9090');    soket.connect('178.254.3.95', '1500');    checkError(soket);     soket.SendString('Bob');       soket.Free;       writeln('Wait from Allice...');    soket:= TUDPBlockSocket.Create;    soket.bind('0.0.0.0', '9090');    repeat    if soket.CanRead(3000) then begin        s:= soket.RecvPacket (3000);        break;      end;      sleep(100);    until false;     writeln(s);    Soket.Free;end.  SERVER: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODE OBJFPC}{$H+}uses    blcksock, sockets, Synautil, SysUtils, HTTPSend, synaip, Process, synamisc, nettools, crt; type    TclientInfo = record                    ip:string;                    port:string;                end;var    soket : TUDPBlockSocket;                               clientAllice, clientBob : TclientInfo;    s : string; begin        soket:= TUDPBlockSocket.Create;    soket.bind('0.0.0.0', '1500');    checkError(soket);      writeln('Waiting for Allice...');    repeat s:= soket.RecvPacket(3000); until s = 'Allice';    clientAllice.ip:= soket.GetRemoteSinIP;    clientAllice.port:= inttostr(soket.GetRemoteSinPort);    writeln('Allice at ', clientAllice.ip, ':', clientAllice.port);       writeln('Waiting for Bob...');    repeat s:= soket.RecvPacket(3000); until s = 'Bob';    clientBob.ip:= soket.GetRemoteSinIP;    clientBob.port:= inttostr(soket.GetRemoteSinPort);    writeln('Bob at ', clientBob.ip, ':', clientBob.port);       soket.Free;      soket:= TUDPBlockSocket.Create;    soket.bind('0.0.0.0', '1500');     writeln('send to Allice ', clientBob.ip, ':', clientBob.port);    soket.connect(clientAllice.ip, clientAllice.port);    s:=clientBob.ip + ':' + clientBob.port;    soket.SendString(s); {    writeln('send to Bob ', clientAllice.ip, ':', clientAllice.port);    soket.connect(clientBob.ip, clientBob.port);    s:=clientAllice.ip + ':' + clientAllice.port;    soket.SendString(s); }    readln;    soket.Free;end.  YOU WRONG!!!!!!!! this can't work because in line 25 of BOB i do a bind and it gets no connections from outside! pls write back ---------------------------------------------------------------------- Warfley · 2023-05-31 22:48 Try sending more packages from both Alice and Bob until you get through (the timeout of 3 seconds is way to high, make it a few Ms or so in your punching loop). Also note that both parties, Alice and Bob need to punch a hole if both are behind a nat. And as a last note hole punching does not work with all nats in all configurations, e.g. when your punching message is discarded by the other nat, it may send an ICMP response, which most nats ignore, but some may use this event to close the punched hole. In those cases you just need to try a few times in rapid succession to trying to send messages on both sides that make it through in the small window of time the hole is open (as soon as both have received a message the hole will stay open). The problem is all routers have different implementations of their nats, and what works with one may not work with another. After all hole punching is a hack, and not really intended behavior. currently I have no setup to test it myself, but here is an example C code that I know from a few years ago works ok: https://github.com/mwarning/UDP-hole-punching-examples/tree/master As you can see it just does register itself at the server, and then when the server sends the address of a new peer it just spams a few messages until the connection can be established. You should also be able to translate this quite easily to pascal, either using the sockets u it (which provides the same sockets API used by the C code, so you can basically copy it's flow 1-1), or use the more abstracted synapse classes and methods ---------------------------------------------------------------------- Key-Real · 2023-06-01 09:54 This doesn't work https://github.com/mwarning/UDP-hole-punching-examples/tree/master I compiled it with GCC on both machines. It connects to the server and get the addresses but can't receive messages from each other. I mean Skype can do this / Games do this, why we can't? ---------------------------------------------------------------------- rvk · 2023-06-01 10:40 Quote from: Key-Real on June 01, 2023, 11:54:54 amI mean Skype can do this / Games do this, why we can't? Are you sure Skype (and those games) really make a direct connection with each other (in your situation)? It could be that an intermediate server is used (if hole punching fails, Skype will use a central server). Quote from: Key-Real on May 30, 2023, 10:24:27 amdon't think I haven't look at the discussion at https://forum.lazarus.freepascal.org/index.php/topic,52110.45.html they tell just do a socket.connect(remoteIP, remotePort); this doesn't work I was part of that discussion. I'm sure that's not the only thing function which was called. Did you try those examples? For us it worked. Although it was not my code and it has some clutter which you wouldn't need. But did you test it? Quote from: Key-Real on June 01, 2023, 11:54:54 amThis doesn't work https://github.com/mwarning/UDP-hole-punching-examples/tree/master I compiled it with GCC on both machines. It connects to the server and get the addresses but can't receive messages from each other. It says in the README.md there: QuoteThe examples have not been successfully tested and are probably broken! ---------------------------------------------------------------------- Key-Real · 2023-06-01 11:18 @RVK Should the Lazarus example of Yours write "recv[ip:port]" when You press the "Start P2P" Button? It does not! SERVER LOG: unching_controller UDP Hole Punching Controller MAX_CONNECTIONS: 100 MAX_INACTIVE_MS: 30000 SOURCE_PORT: 1985 PWD: ---- Start print ---- -------------------------------- 3r2SSU 1-6-23 13:24:05 -------------------------------- ---- End print ---- ---- Start print ---- -------------------------------- 3r2SSU 1-6-23 13:24:22 -------------------------------- -------------------------------- BE4m0k 1-6-23 13:24:22 -------------------------------- ---- End print ---- ---- Start print ---- -------------------------------- 3r2SSU 1-6-23 13:24:25       B --> 84.46.70.42:13512 -------------------------------- -------------------------------- BE4m0k 1-6-23 13:24:25 -------------------------------- ---- End print ---- ---- Start print ---- -------------------------------- 3r2SSU 1-6-23 13:24:28       B --> 84.46.70.42:13512 -------------------------------- -------------------------------- BE4m0k 1-6-23 13:24:28       B --> 185.237.102.35:35433 -------------------------------- ---- End print ---- ---------------------------------------------------------------------- Key-Real · 2023-06-01 12:24 Quote from WikiPedia: VoIP products, online gaming applications, and P2P networking software all use hole punching.      - Telephony software Skype uses hole punching to allow users to communicate with one or more users audibly.      - Fast-paced online multi-player games may use a hole punching technique or require users to create a permanent firewall pinhole in order to reduce network latency.      - VPN applications such as Hamachi, ZeroTier, and Tailscale utilize hole punching to allow users to connect directly to subscribed devices behind firewalls.      - Decentralized peer-to-peer file sharing software relies on hole punching for file distribution. So what they do, what we do wrong? ---------------------------------------------------------------------- Warfley · 2023-06-01 13:04 Quote from: rvk on June 01, 2023, 12:40:22 pmIt says in the README.md there: QuoteThe examples have not been successfully tested and are probably broken! Back in Uni I used this as a "guide" to create some working hole punching code in C++. I don't have that code anymore, otherwise I would have posted it, but in general this can work. Quote from: Key-Real on June 01, 2023, 02:24:28 pmSo what they do, what we do wrong? But that said, as I already wrote previously, different routers behave differently, and hole punching is easier in some networks than others. It's a hack. This SO post summarizes it quite well: https://stackoverflow.com/a/6794529 You can try different sending patterns, e.g. send in bursts with some pauses in between, which seems to help sometimes ---------------------------------------------------------------------- rvk · 2023-06-01 14:17 I have some python code which I used to test if punching a whole was really possible in my situation. Converted to python3 (original code was here https://gist.github.com/somic/224795#file-udp_hole_punch_tester-py) For me that still worked. I'll need to look at the other code here as to why that doesn't work. Code: Python  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i #!/usr/bin/env python## udp_hole_punch_tester.py - UDP Hole Punching test tool## Usage: udp_hole_punch_tester.py remote_host remote_port## Run this script simultaneously on 2 hosts to test if they can punch# a UDP hole to each other.## * remote_port should be identical on 2 hosts.# * if remote_port < 1024, must be root.# * tested on python 2.5.## Copyright (C) 2009 Dmitriy Samovskiy, http://somic.org## License: Apache License, Version 2.0#          http://www.apache.org/licenses/# import sys, os, time, socket, randomfrom select import select def log(*args):    print(time.asctime(), ' '.join([str(x) for x in args])) def puncher(remote_host, port):    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)    sock.bind(('', port))     my_token = str(random.random())    log("my_token =", my_token)    remote_token = "_"     sock.setblocking(0)    sock.settimeout(5)     remote_knows_our_token = False     for i in range(60):        r,w,x = select([sock], [sock], [], 0)         if remote_token != "_" and remote_knows_our_token:            log("we are done - hole was punched from both ends")            break         if r:            data, addr = sock.recvfrom(1024)            log("recv:", data)            if remote_token == "_":                remote_token = data.split()[0]              log("remote_token is now", remote_token)            if len(data.split()) == 3:                log("remote end signals it knows our token")                remote_knows_our_token = True       if w:            data = "%s %s" % (my_token, remote_token)            if remote_token != "_": data += " ok"            log("sending:", data)            dd = bytes(data, 'utf-8')            sock.sendto(dd, (remote_host, port))            log("sent", i)        time.sleep(0.5)     log("done")    sock.close()     return remote_token != "_" if __name__ == '__main__':    remote_host = sys.argv[1]    port = int(sys.argv[2])     if puncher(remote_host, port):        log("Punched UDP hole to %s:%d successfully" % (remote_host, port))    else:        log("Failed to punch hole") ---------------------------------------------------------------------- rvk · 2023-06-01 14:43 Quote from: rvk on June 01, 2023, 04:17:33 pmFor me that still worked. I'll need to look at the other code here as to why that doesn't work. What I find weird about this code is that it works without any port translation. I even tried it on port 1500 UDP which I have redirected to another computer. And then it still works. So just continuously pushing a string to the other side should be enough. Edit: Ok, that only works if you are really on two different locations. If you are on the same location and use your external address the NAT kicks in and translates the port and it doesn't work with the same port anymore. ---------------------------------------------------------------------- Key-Real · 2023-06-01 16:12 I translated the https://github.com/mwarning/UDP-hole-punching-examples/tree/master GOOD and BAD NEWS! the good is it works the Bad is not everywhere today morning I tried this the whole time at work. I opened one client on the works internet connection and the other through my mobile phone. the server gets the messages from them, but the clients are unable to connect to each other. so I tried this at home. I had the same scenario. So I called a friend to open a client on his side and I open one on my home machine trough my internet provider. And look, it works. So I did this again but now through my mobile. Hire is the bad news. I can connect to my friends client but he can't to my mobile I mark this Topic as solved. here the code: SERVER: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODE OBJFPC}{$H+}uses    sockets, SysUtils, crt; function inet_ntoa(addr:in_addr):Pchar;cdecl;external 'libc' name 'inet_ntoa'; procedure diep(s:string);begin    writeln(s);    halt(1);end; type      Tclient = record                host : dword;              port : word;               end; var    si_me, si_other : sockaddr_in;    s, i, j, slen : dword;    buf : array [0..512] of char;    clients : array [0..10] of Tclient; // 10 clients. Notice that we're not doing any bound checking.    n : longint;   begin    slen:= sizeof(si_other);     // Create a UDP socket    s:= fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);    if s = -1 then diep('socket');      // si_me stores our local endpoint. Remember that this program    // has to be run in a network with UDP endpoint previously known    // and directly accessible by all clients. In simpler terms, the    // server cannot be behind a NAT.    fillchar(si_me, 0, sizeof(si_me));    si_me.sin_family:= AF_INET;    si_me.sin_port:= htons(9930);    si_me.sin_addr.s_addr:= htonl(INADDR_ANY);    if fpbind(s, @si_me, sizeof(si_me)) = -1 then diep('bind');        while n < 2 do begin            // When a new client sends a datagram...        if fprecvfrom(s, @buf, 512, 0, @si_other, @slen) = -1 then diep('recvfrom');        // The client's public UDP endpoint data is now in si_other.        // Notice that we're completely ignoring the datagram payload.        // If we want to support multiple clients inside the same NAT,        // we'd have clients send their own private UDP endpoints        // encoded in some way inside the payload, and store those as        // well.        writeln('Received packet from ', inet_ntoa(si_other.sin_addr), ':', ntohs(si_other.sin_port));        // Now we add the client's UDP endpoint in our list.        clients[n].host:= si_other.sin_addr.s_addr;        clients[n].port:= si_other.sin_port;        inc(n);        // And then tell everybody about everybody's public UDP endpoints        for i:=0 to n - 1 do begin                  si_other.sin_addr.s_addr:= clients[i].host;            si_other.sin_port:= clients[i].port;            // We send a datagram for each client in our list. Of course,            // we could also assemble a single datagram and send that.            for j:= 0 to n - 1 do begin                // The payload is the client's public UDP endpoint, clients[j]                writeln('Sending to ', inet_ntoa(si_other.sin_addr), ':', ntohs(si_other.sin_port));              // We're sending binary data here, using the server's byte order.                // In your code, you should make sure every client agrees on the endianness.                if fpsendto(s, @clients[j], 6, 0, @si_other, slen) = -1 then diep('sendto');          end;        end;        writeln('Now we have ', n, ' clients');      // And we go back to listening. Notice that since UDP has no notion        // of connections, we can use the same socket to listen for data        // from different clients.    end;     // Actually, we never reach this point...    closeSocket(s);end.  CLIENT: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$MODE OBJFPC}uses    sockets, SysUtils{$IFDEF WINDOWS}    , winsock{$ENDIF}    ;  {$IFNDEF WINDOWS}function inet_aton(cp:Pchar; addr:Pin_addr):longint;cdecl;external 'libc' name 'inet_aton';function inet_ntoa(addr:in_addr):Pchar;cdecl;external 'libc' name 'inet_ntoa';{$ENDIF}  const SRV_IP = '178.254.3.95';  procedure diep(s:string);begin    writeln(s);    halt(1);end; type          Tclient = record                        host : dword;                      port : word;                   end; var        si_me, si_other : sockaddr_in;            s, i, f, j, k, slen : dword;    buf, server : Tclient;     peers : array [0..10] of Tclient;    n : longint;begin         slen:= sizeof(si_other);         s:= fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);        if s = -1 then diep('socket');  // Our own endpoint data    fillchar(si_me, 0, sizeof(si_me));    si_me.sin_family:= AF_INET;    si_me.sin_port:= htons(9930); // This is not really necessary, we can also use 0 (any port)    si_me.sin_addr.s_addr:= htonl(INADDR_ANY);      // The server's endpoint data    fillchar(si_other, 0, sizeof(si_other));    si_other.sin_family:= AF_INET;    si_other.sin_port:= htons(9930);{$IFDEF WINDOWS}    si_other.sin_addr.s_addr:=inet_addr(SRV_IP);{$ELSE}    if inet_aton(SRV_IP, @si_other.sin_addr) = 0 then diep('aton');{$ENDIF}     // Store the server's endpoint data so we can easily discriminate between server and peer datagrams.    server.host:= si_other.sin_addr.s_addr;    server.port:= si_other.sin_port;         // Send a simple datagram to the server to let it know of our public UDP endpoint.    // Not only the server, but other clients will send their data through this endpoint.    // The datagram payload is irrelevant, but if we wanted to support multiple    // clients behind the same NAT, we'd send our won private UDP endpoint information    // as well.    if fpsendto(s, pchar('hi'), 2, 0, @si_other, slen) = -1 then diep('sendto');        // Right here, our NAT should have a session entry between our host and the server.    // We can only hope our NAT maps the same public endpoint (both host and port) when we    // send datagrams to other clients using our same private endpoint.  while true do begin            // Receive data from the socket. Notice that we use the same socket for server and        // peer communications. We discriminate by using the remote host endpoint data, but        // remember that IP addresses are easily spoofed (actually, that's what the NAT is        // doing), so remember to do some kind of validation in here.        if fprecvfrom(s, @buf, sizeof(buf), 0, @si_other, @slen) = -1 then diep('recvfrom');         writeln('Received packet from ', inet_ntoa(si_other.sin_addr), ':', ntohs(si_other.sin_port));        if (server.host = si_other.sin_addr.s_addr) and (server.port = si_other.sin_port) then begin                    // The datagram came from the server. The server code is set to send us a            // datagram for each peer, in which the payload contains the peer's UDP            // endpoint data. We're receiving binary data here, sent using the server's          // byte ordering. We should make sure we agree on the endianness in any            // serious code.            f:= 0;            // Now we just have to add the reported peer into our peer list            for i:= 0 to n do begin                            if (peers[i].host = buf.host) and (peers[i].port = buf.port) then begin                                  f:= 1;                end;            end;            // Only add it if we didn't have it before.            if f = 0 then begin                peers[n].host:= buf.host;                peers[n].port:= buf.port;                inc(n);            end;          si_other.sin_addr.s_addr:= buf.host;            si_other.sin_port:= buf.port;            writeln('Added peer ', inet_ntoa(si_other.sin_addr), ':', ntohs(si_other.sin_port));          writeln('Now we have ', n, ' peers');            // And here is where the actual hole punching happens. We are going to send          // a bunch of datagrams to each peer. Since we're using the same socket we            // previously used to send data to the server, our local endpoint is the same            // as before.            // If the NAT maps our local endpoint to the same public endpoint          // regardless of the remote endpoint, after the first datagram we send, we            // have an open session (the hole punch) between our local endpoint and the            // peer's public endpoint. The first datagram will probably not go through            // the peer's NAT, but since UDP is stateless, there is no way for our NAT          // to know that the datagram we sent got dropped by the peer's NAT (well,            // our NAT may get an ICMP Destination Unreachable, but most NATs are            // configured to simply discard them) but when the peer sends us a datagram,            // it will pass through the hole punch into our local endpoint.            for k:=0 to 10 - 1 do begin                // Send 10 datagrams.                for i:=0 to n - 1  do begin                                    si_other.sin_addr.s_addr := peers[i].host;                    si_other.sin_port := peers[i].port;                    // Once again, the payload is irrelevant. Feel free to send your VoIP                  // data in here.                     writeln('sending to ', inet_ntoa(si_other.sin_addr), ':', ntohs(si_other.sin_port ));                   if fpsendto(s, pchar('hi'), 2, 0, @si_other, slen) = -1 then diep('sendto()');                end;            end;           writeln('packages send');         end else begin                    // The datagram came from a peer            for i:= 0 to n - 1 do begin                          // Identify which peer it came from              //if (peers[i].host = buf.host) and (peers[i].port = buf.port) then begin                                    // And do something useful with the received payload                    writeln('Received from peer ', i);                //    break;               // end;          end;             // It is possible to get data from an unregistered peer. These are some reasons            // I quickly came up with, in which this can happen:            // 1. The server's datagram notifying us with the peer's address got lost,            //  or it hasn't arrived yet (likely)            // 2. A malicious (or clueless) user is sending you data on this endpoint (maybe            //    unlikely)            // 3. The peer's public endpoint changed either because the session timed out,            //    or because its NAT did not assign the same public endpoint when sending            //    datagrams to different remote endpoints. If this happens, and we're able            //    to detect this situation, we could change our peer's endpoint data to            //    the correct one. If we manage to pull this off correctly, even if at most            //    one client has a NAT that doesn't support hole punching, we can communicate            //    directly between both peers.        end;    end;     // Actually, we never reach this point...    closeSocket(s);end. ---------------------------------------------------------------------- Key-Real · 2023-06-01 16:14 btw. Ideas what is the background behind this issue? Is it possible to solve? ---------------------------------------------------------------------- rvk · 2023-06-01 16:28 Quote from: Key-Real on June 01, 2023, 06:14:04 pmIdeas what is the background behind this issue? Method of NAT implementation. Something like this: QuoteUDP hole punching will not work with symmetric NAT devices (also known as bi-directional NAT) which tend to be found in large corporate networks. In symmetric NAT, the NAT's mapping associated with the connection to the known STUN server is restricted to receiving data from the known server, and therefore the NAT mapping the known server sees is not useful information to the endpoint.https://en.wikipedia.org/wiki/UDP_hole_punching So with a stricter NAT, only successful connections are registered as allowed connections. And since client1 and client2 never establish a connection to each other, they can't get through. ---------------------------------------------------------------------- Warfley · 2023-06-01 17:56 Quote from: Key-Real on June 01, 2023, 06:12:20 pmthe server gets the messages from them, but the clients are unable to connect to each other. so I tried this at home. I had the same scenario. So I called a friend to open a client on his side and I open one on my home machine trough my internet provider. And look, it works. So I did this again but now through my mobile. Hire is the bad news. I can connect to my friends client but he can't to my mobile Have you checked if all the ports and addresses are correct? Because one of the main problems with network programming is actually byte order (which as stated in the comments is ignored in this example). If your phone and your Desktop have different byte orders, they may interpret the data differently. So you should always use network byte order to send data and convert it upon receival. The ntohl function converts a long (i.e. 32 bit) integer from network byte order to host byte order. htonl is the opposite converting host to network. Also, just to note, you are using inet_ntoa which is basically the C version of IntToStr for inet addresses. You can just use the function NetAddrToStr (HostAddrToStr for Host byte order, but socket addresses are in network byte order) instead. Lastly do you use a wifi with you phone or mobile network? Because Mobile Networks are quite comple. Basically in order for you not to change your IP address every time you connect to a new tower, the datacenters at each station that doe the tunneling of data, are connected through one giant NAT network, a so called Large Scale Nat. They have higher security standards than your usual home router NATs and are usually symmetric NATs. With google I have found this link: https://drive.google.com/file/d/0B 1IimJ20gG0SY2NvaE4wRVVMbG8/view?resourcekey=0-C9_6uLzao8eCEwa1qRhv7w as well as this paper behind a paywall (which may or may not be circumventable via scihub): http://dx.doi.org/10.7125/APAN.30.5