[CLOSED] synapse udp bidirection ip port bind? lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- senglit · 2022-03-14 16:36 hi all, After digging a little bit on the forum especially this thread: https: //forum.lazarus.freepascal.org/index.php/topic,37868.msg256432.html#ms g256432 I thought it should be clear about how to make a client/server udp program. And it turned out that I was wrong. My server works on win10 and client works on arm linux. When the client starts up it start to send message to server in certain interval. when the server gets the message, it stores the ip and port of the client and sends message to client via the ip and port when needs. A thread on server to receive message: 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 procedure TUdpReceiver.Execute;var  sock: TUDPBlockSocket;  buf: string;  DataSize: integer;  func: HeyinUdpFunctionCode;begin  sock := TUDPBlockSocket.Create;  //sock.CreateSocket;  sock.EnableReuse(True);  try    sock.Family := SF_IP4;    sock.bind('0.0.0.0', udpPort.ToString);    if sock.LastError = 0 then begin      while not terminated do begin        buf := sock.RecvPacket(1000);        DataSize := Length(buf);        if DataSize < 3 then continue;        if not CheckCrc16(buf[1], DataSize) then continue;        if DataSize <> SizeOf(TBoxReport) then continue;        func := HeyinUdpFunctionCode(Ord(buf[1]));        if func = hufBoxReport then begin          boxReport(buf, sock.GetRemoteSinIP, sock.GetRemoteSinPort);  // here save the ip and port of this client        end;      end;    end;    sock.CloseSocket;  finally    sock.Free;  end;end;     Sending procedure on the 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 procedure subscribeBox(sn: word; onoff: boolean; token: string);var  buf: string;  sock: TUDPBlockSocket;  ip: string;  port: word;  b: PBoxOnline;  cmd: TCmdToBox;begin  if badToken(token) then exit;  cmd.func := hufBoxSubScribe;  if onoff then begin    cmd.Data := 1;  end else begin    cmd.Data := 0;  end;  SetLength(buf, sizeof(cmd));  move(cmd, buf[1], sizeof(cmd));  fillCrc16(buf[1], sizeof(cmd));  for b in boxConnList do begin    if b^.sn = sn then begin      sock := TUDPBlockSocket.Create;      //sock.CreateSocket;      sock.EnableReuse(True);      if sock.LastError = 0 then begin        try          sock.SetRemoteSin(b^.ip, b^.port.ToString);          sock.SendString(buf);          sock.CloseSocket;        finally        sock.Free;        end;      end;      break;    end;  end;end; Receiving thread on 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 procedure TUdpReceiver.Execute;var  Sock: TUDPBlockSocket;  buf: string;  DataSize: integer;  cmd: TCmdToBox;begin  sock := TUDPBlockSocket.Create;  sock.CreateSocket;  sock.EnableReuse(True);  try    sock.Family := SF_IP4;    sock.bind('0.0.0.0', svrPort.ToString); //both client and svr use the same port    if sock.LastError = 0 then begin      sock.SetRemoteSin(svrIP, svrPort.ToString);      sock.SendString('first message, to do the punchhole action');      while not terminated do begin        Inc(udpReceiverDog);        buf := sock.RecvPacket(1000);        DataSize := Length(buf);        if DataSize <> SizeOf(cmd) then continue;        if not CheckCrc16(buf[1], DataSize) then continue;      move(buf[1], cmd, SizeOf(cmd));        if cmd.func = hufBoxReboot then begin          // 0 reboot the box        end else if cmd.func = hufBoxCoilOn then begin           WriteCoil(1, 1, cmd.Data, True);        boxLog('Ctrl- Coil on ' + cmd.Data.ToString);        end else if cmd.func = hufBoxCoilOff then begin           WriteCoil(1, 1, cmd.Data, False);          boxLog('Ctrl- Coil off ' + cmd.Data.ToString);        end else if cmd.func = hufBoxSubScribe then begin           subscribed := (cmd.Data = 1);          if subscribed then boxLog('subscribed')          else            boxLog('unsubscribed');        end;      end;    end;    sock.CloseSocket;  finally    sock.Free;  end;end;       Sending procedure on 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 procedure TUdpSender.sendReport;var  sock: TUDPBlockSocket;  buf: string;begin  SetLength(buf, SizeOf(TBoxReport));  sock := TUDPBlockSocket.Create;  sock.CreateSocket;  sock.EnableReuse(True);  if sock.LastError = 0 then begin    try                       sock.Family := SF_IP4;                           sock.bind(myIP, svrPort.ToString);   //myIP is client eth0 ip get by TProcess ifconfig      move(myReport, buf[1], SizeOf(TBoxReport));      FillCrc16(buf[1], SizeOf(TBoxReport));      sock.SetRemoteSin(svrIP, svrPort.ToString);      sock.SendString(buf);       sock.CloseSocket;    finally      sock.Free;    end;  end;end;  Now, my server connect to routerA, client connect to routerB, routerB connect to  routerA. routerA ip: 192.168.115.1 server ip: 192.168.115.102 routerB ip: 192.168.200.1 client eth0 ip: 192.168.200.214 (ifconfig, eth0) routerA give this ip to routerB: 192.168.115.212 with my code, the server can receive the message and get the ip of the client(192.168.115.212:8765). The port is right. the ip looks like ok since it's the routerB ip. then my server send back the message to 192.168.115.212:8765, but the client can't get it! I think after the client send the first message to the server it already did the 'punchhole' action on routerB. Actually, at the client side even the receive thread didn't send this first punchhole message, the sender will send reports to the server at least each 1 minutes. The hole is there, why the client fail to receive from server? What's wrong did I do? PS: I think what's important for the punchhole action is port not ip. So I also tried to change the bind code of client sending procedure: 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 // sock.bind(myIP, svrPort.ToString);     // old, sending is ok with this.sock.SocksPort:=svrPort.ToString;              // new 1, sending fail with thisor this one:sock.bind('0.0.0.0',svrPort.ToString)     //new 2, same ip:port with receiving thread, sending fail with this Then the server can't receive message at all! I have to change it back. ---------------------------------------------------------------------- MarkMLl · 2022-03-14 19:19 What is this punchbowl to which you refer? MarkMLl ---------------------------------------------------------------------- Thaddy · 2022-03-14 19:39 Quote from: MarkMLl on March 14, 2022, 08:19:25 pmWhat is this punchbowl to which you refer? MarkMLl It is not drinkable, Mark. He refers to hole punching and tries to do that on a single port. He needs two ports, one for the handshake and the second for work traffic. And he needs to set up TLS 1.2,because without secure connections hole punching won't work, furthermore he should allow redirects. If you did not know that: it is a way to set up a server / client connection where one or both are behind a firewall and/or distributed servers. Actually a more correct name for this is "protocol tunneling". Almost all popular internet apps use this technique e.g. everything google, skype, telegram, the media apps etc, etc. Since he does not adhere to the above requirements his code will never work. ---------------------------------------------------------------------- MarkMLl · 2022-03-14 19:54 Quote from: Thaddy on March 14, 2022, 08:39:48 pmIt is not drinkable, Mark. ... Since he does not adhere to the above requirements his code will never work. But you must admit that it had the effect of keeping his problem visible until somebody familiar with his terminology spotted it :-) I suspected it was something like that, particularly since he mentioned routers. I suppose the first question is whether a ping works in both directions, followed by "if ping works why doesn't UDP?". MarkMLl ---------------------------------------------------------------------- senglit · 2022-03-15 02:34 This is how I hope my app may work: 1. There is 1 server run on internet with an public ip address 2. There are some clients. They work in different LANs which can access to the internet with a router(or several routers at different level). 3. When a client start up, it talks to the server by udp. then the router give an internet ip:port(which can be accessed by apps on internet out of the client's lan) to the client. 4. when client A need to talk to client B, it can get the internet ip:port of client B from server and then send the message. At this phase, I pretend server ip 192.168.115.102 is a public ip address, and client connect to RouterB works like in a LAN with ip 192.168.200.214. The client sends a message to the server and the server recieved this udp message from 192.168.115.212. But the client can't receive the message back from the server. ---------------------------------------------------------------------- senglit · 2022-03-16 09:40 I think I find what's wrong. Since the client sender must do bind action, the server sender should do it too. After I add the bind action it works. 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   for b in boxConnList do begin    if b^.sn = sn then begin      sock := TUDPBlockSocket.Create;      sock.EnableReuse(True);      sock.bind('0.0.0.0', udpPort.ToString);  // must do bind. bind to '0.0.0.0' is ok, but bind '127.0.0.1' does not work      if sock.LastError = 0 then begin        try          sock.SetRemoteSin(b^.ip, b^.port.ToString);          sock.SendString(buf);          sock.CloseSocket;          writeln('sent cmd to ', b^.ip, ':', b^.port.ToString);        finally          sock.Free;        end;      end;      break;    end;  end; ---------------------------------------------------------------------- MarkMLl · 2022-03-16 10:02 Quote from: senglit on March 16, 2022, 10:40:23 amI think I find what's wrong. Since the client sender must do bind action, the server sender should do it too. After I add the bind action it works. I think you need to use it for code that is waiting for a UDP packet, although it might depend to some extent on the precise details of the operating system (not all variants of the Berkeley sockets API are created equal). If all you're doing is sending a packet then you can use fpSendTo(), which takes a socket and an address structure as its parameters. So fpBind() tells the OS that incoming packets are to be enqueued, fpRecvFrom() dequeues a packet and fills in the sender's address etc., and fpSendTo() requires the receiver's address as a parameter. It's not too bad on a local network. It gets hairy if routers are involved, and in particular you need to know that UDP broadcasts are generally blocked at a router (I've had to do some very hairy stuff with proxies in the past). MarkMLl