Jump to content
philipp.hofmann

TIdUDPListenerThread.UDPRead is not reached on MacOS 11 (but 10)

Recommended Posts

Hi,

 

I'm searching for the reason that TIdUDPListenerThread.UDPRead is not reached in my Tethering app if I run the app under MacOS 11. It's fine with MacOS 10.

The FBindind.Select(AcceptWait) is always return false on MacOS11.

 

How can I solve this?

 

Best regards, Philipp

 

 

 

TetheringDemo.zip

Share this post


Link to post

Did you verify that your UDP listening port is actually open, and that inbound data is actually reaching that port?  If Select() returns false, it means the underlying OS reported that no data was available before the specified timeout had elapsed.  What is your AcceptWait set to exactly?

Share this post


Link to post

The command "nc -vnzu 192.168.10.30 2020-2040" returns "[udp/*] succeeded!" for all ports. But also if the app is not running yet and it's the same response on MacOS 10 as on MacOS 11.
I'm using the Delphi-Tethering-implementation and it looks like Start-Listing was successful. 
FAcceptWait contains the default value, so 1000.

 

I'm wondering if there is any new setting to use for MacOS 11 (e.g. capabilities). I know the Multicast-capability yet but this is only for iOS14.x.

 

Share this post


Link to post

 

On 6/14/2021 at 6:31 PM, Remy Lebeau said:

Did you verify that your UDP listening port is actually open, and that inbound data is actually reaching that port?  If Select() returns false, it means the underlying OS reported that no data was available before the specified timeout had elapsed.  What is your AcceptWait set to exactly?

Hi Remy,
do you have any other idea what to check on my side to solve the MacOS11 issue?
Can you reproduce on your side the issue with MacOS11?
Best regards, Philipp

Share this post


Link to post
3 hours ago, philipp.hofmann said:

do you have any other idea what to check on my side to solve the MacOS11 issue?

No.  But, on the other hand, what you provided in your last reply didn't really answer my questions at all.  Did you VERIFY (ie, via netstat or lsof) that the IP/Port your server listens on is actually open successfully?  Did you VERIFY (ie, with a packet sniffer) that network traffic is actually reaching your listening IP/Port?

Quote

Can you reproduce on your side the issue with MacOS11?

I'm not a Mac developer.  I have no way to test this.  But BSD-style socket APIs, like the one Indy uses internally, are largely similar/consistent across different platforms.

Edited by Remy Lebeau

Share this post


Link to post

Hi Remy,

a) I've checked now with "netstat -an".

 

On MacOS10 I get the following entries:

 

Proto  Recv-Q Send-Q Local Address           Foreign Address        (state)

tcp4    0           0           *.2020                       *.*                              LISTEN
udp4   0          0            *.2020                       *.*

 

On MacOS11 I get the following entry only:

Proto  Recv-Q Send-Q Local Address           Foreign Address        (state)

tcp4    0           0           *.2020                       *.*                              LISTEN

but there is no entry for *.2020.

 

I can see with Wireshark-Packet-Sniffer that the UDP package for Port 2020 is there but not read on MacOS11 computer.

 

Best regards, Philipp

Share this post


Link to post
5 minutes ago, philipp.hofmann said:

On MacOS10 I get the following entries:

On MacOS11 I get the following entry only:

but there is no entry for *.2020.

Interesting.  I'm surprised that no error is being raised if TIdUDPServer is having trouble opening port 2020.  Makes me wonder if it is even attempting to open the port in the first place.  I don't know how Tethering uses Indy, can you show the actual code that sets up TIdUDPServer?  Do you have the same problem if you use TIdUDPServer directly?  Do you get an OnBeforeBind event fired for the Binding assigned to port 2020?

5 minutes ago, philipp.hofmann said:

I can see with Wireshark-Packet-Sniffer that the UDP package for Port 2020 is there but not read on MacOS11 computer.

Well, that makes sense, if there is no port being opened to accept the packets.

Share this post


Link to post

The OnBeforeBind is requested but not assigned. But this is also the case under Windows and seems to be no problem.

The code you are asking for should be

constructor TTetheringNetworkServerCommUDP.Create(AIPVersion: TCommIPVersion; const ABindToAddress: string);
var
  I: Integer;
  LSocket: IIPSocketHandle;
  LSubnetTable: TArray<TIPv4Subnet>;
begin
  inherited Create;
  FIPVersion := AIPVersion;
  FUDPServer := PeerFactory.CreatePeer('', IIPUDPServer, nil) as IIPUDPServer;
  FUDPServer.ThreadedEvent := True;
  FUDPServer.OnRead := DoUDPRead;
  FUDPServer.OnException := DoUDPException;
  FUDPServer.IPVersion := FIPVersion;

  if ABindToAddress <> '' then
  begin
    FSocketUDP := FUDPServer.Bindings.Add;
    FSocketUDP.IP := ABindToAddress;
    FSocketUDP.IPVersion := FIPVersion;
  end
  else
  begin
    LSubnetTable := GStackPeers.GetIPv4Subnets;
    if Length(LSubnetTable) < 2 then
    begin
      FSocketUDP := FUDPServer.Bindings.Add;
      FSocketUDP.IPVersion := FIPVersion;
    end
    else
    begin
      for I := Low(LSubnetTable) to High(LSubnetTable) do
      begin
        LSocket := FUDPServer.Bindings.Add;
        LSocket.IPVersion := FIPVersion;
        LSocket.IP := LSubnetTable[I].Address;
        if not assigned(FSocketUDP) then
          FSocketUDP := LSocket;
      end;
    end;
  end;
end;
                       
function TTetheringNetworkServerCommUDP.DoStartServer: Boolean;
begin
  Result := True;
  try
    FUDPServer.Active := True;
  except
    // This is a non conventional method to catch an exception that is in a library that we do not want to have a dependency.
    // This is considered a HACK and not a good sample of programming techniques.
    // We are going to let this code handle the exceptions until we add a proper mechanism to propagate the
    // Indy exceptions through the IPPeerAPI in an ordered and safe manner.
    on E: Exception do
    begin
      Result := False;
      if not (CheckExceptionName(E, 'EIdCouldNotBindSocket') or CheckExceptionName(E, 'EIdSocketError')) then // Do not translate
        raise;
    end;
  end;
end;

Do you have an example how to use TIdUDPServer directly? I'm using the Delphi Tethering implementation to hide such complicate issues from me. And now I'm in as I try to solve an error.

Share this post


Link to post
52 minutes ago, philipp.hofmann said:

The OnBeforeBind is requested but not assigned.

I don't understand what you mean.  Please clarify.  Is the event assigned a handler?  If so, is the handler being called?  If so, is the provided TIdSocketHandle object filled in with the correct port information?

52 minutes ago, philipp.hofmann said:

The code you are asking for should be

I don't see any port number being assigned in that code.  So, unless the port number is assigned elsewhere after TTetheringNetworkServerCommUDP.Create() exits and before TTetheringNetworkServerCommUDP.DoStartServer() is called, then the UDP server will end up binding to a random port for each entry created in FUDPServer.Bindings.

52 minutes ago, philipp.hofmann said:

Do you have an example how to use TIdUDPServer directly?

Really?

uses
  ..., IdSocketHandle, IdUDPSerrver;

...

var
  UDPServer: TIdUDPServer;

...

UDPServer := TIdUDPServer.Create;
UDPServer.ThreadedEvent := True;
UDPServer.OnRead := DoUDPRead;
UDPServer.OnException := DoUDPException;
UDPServer.IPVersion := Id_IPv4;
with UDPServer.Bindings.Add do
begin
  IP := '192.168.10.30;
  Port := 2020;
end;
UDPServer.Active := True;

...

procedure TMyForm.DoUDPRead(AThread: TIdUDPListenerThread;
  const AData: TIdBytes; ABinding: TIdSocketHandle);
begin
  ...
end;

procedure TMyForm.DoUDPException(AThread: TIdUDPListenerThread;
  ABinding: TIdSocketHandle; const AMessage : String;
  const AExceptionClass : TClass);
begin
  ...
end;

 

Share this post


Link to post
Quote

I don't understand what you mean.  Please clarify.  Is the event assigned a handler?  If so, is the handler being called?  If so, is the provided TIdSocketHandle object filled in with the correct port information?

There is no handler assigned to the event. The handler is called.

Quote

I don't see any port number being assigned in that code.  So, unless the port number is assigned elsewhere after TTetheringNetworkServerCommUDP.Create() exits and before TTetheringNetworkServerCommUDP.DoStartServer() is called, then the UDP server will end up binding to a random port for each entry created in FUDPServer.Bindings.

The port is set correctly between both methods in the Tethering implementation. It works perfectly with Windows, Android, iOS 13 and MacOS 10.

 

But now we come to magic: Since today it's running fine on MacOS 11 and iOS 14 also. I haven't updated the code or the OS. But I've updated to newest XCode version this week. This is the only dependency I can imagine that this was the reason. 
But thanks for your help to get a better understanding of the technic below Tethering. If I run again into problems, this could help.

Share this post


Link to post

I have found now the reason: 
- as long as I have a iPad connected to my MacBook via USB-C-Cable, I can't establish a Tethering connection

- as soon as I remove the cable, I can establish a Tethering connection

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×