Lazarus

Programming => Networking and Web Programming => Topic started by: ken_ramsey on August 21, 2019, 05:07:06 am

Title: Multicasting on Windows
Post by: ken_ramsey on August 21, 2019, 05:07:06 am
Hello, I was having difficulty getting multicasting to work in Windows.

I found out that somewhere along the line Microsoft changed the values of the constants used with the IPPROTO_IP flag when you use the setsockopt function. The Winsock packages for FPC have the value for IP_ADD_MEMBERSHIP set to 5, but in my version of the Windows SDK at least, this value is now 12.

So the following sort of snippet fixes it:
Code: Pascal  [Select][+][-]
  1.   {$ifdef MSWINDOWS}
  2.   err := fpsetsockopt(sock, IPPROTO_IP, 12, @group, SizeOf(group));
  3.   {$else}
  4.   err := fpsetsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
  5.          @group, SizeOf(group));
  6.   {$endif}
  7.  

For completeness, here's a complete working example of a simple multicast listener of text-based traffic:

Code: Pascal  [Select][+][-]
  1. program MulticastListener;
  2.  
  3. uses
  4.     Sockets, SysUtils;
  5.  
  6. type
  7.   TImReq = packed record
  8.     imr_multicast:      TInAddr;
  9.     imr_interface:      TInAddr;
  10.   end;
  11.  
  12. var
  13.   sock:             TSocket;
  14.   iface:            TInetSockAddr;
  15.   mcaststr:         String;
  16.   nicstr:           String;
  17.   portno:           Word;
  18.   argno:            Integer;
  19.   err:              Integer;
  20.   opt:              Integer;
  21.   group:            TImReq;
  22.   buflen:           LongInt;
  23.   buf:              Array[1..4096] of Byte;
  24.   msg:              AnsiString;
  25.   nbytes:           ssize_t;
  26.   sender:           TInetSockAddr;
  27.  
  28. procedure Usage;
  29. begin
  30.      WriteLn('Usage: MulticastListener -i iface -m mcastaddr -p portno');
  31.      WriteLn;
  32.      Halt;
  33. end;
  34.  
  35. begin
  36.   argno := 1;
  37.   nicstr := '';
  38.   mcaststr := '';
  39.   portno := 0;
  40.  
  41.   while argno <= ParamCount do
  42.   begin
  43.     if ParamStr(argno) = '-i' then
  44.     begin
  45.       Inc(argno);
  46.       nicstr := ParamStr(argno);
  47.     end
  48.     else if ParamStr(argno) = '-m' then
  49.     begin
  50.       Inc(argno);
  51.       mcaststr := ParamStr(argno);
  52.     end
  53.     else if ParamStr(argno) = '-p' then
  54.     begin
  55.       Inc(argno);
  56.       portno := StrToInt(ParamStr(argno));
  57.     end
  58.     else
  59.         Usage;
  60.     Inc(argno);
  61.   end;
  62.  
  63.   if (portno = 0) or (nicstr = '') or (mcaststr = '') then
  64.      Usage;
  65.  
  66.   sock := fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  67.   if sock = SOCKET_ERROR then
  68.   begin
  69.     WriteLn('fpsocket failed with ', socketerror);
  70.     Halt;
  71.   end;
  72.  
  73.   with iface do
  74.   begin
  75.        sin_family:=AF_INET;
  76.        sin_addr := StrToNetAddr(nicstr);
  77.        sin_port := htons(portno);
  78.   end;
  79.  
  80.   err := fpbind(sock, PSockAddr(@iface), SizeOf(iface));
  81.   if err <> 0 then
  82.   begin
  83.     WriteLn('fpbind failed with ', socketerror);
  84.     Halt;
  85.   end;
  86.  
  87.   opt := 1;
  88.   err := fpsetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, @opt, SizeOf(opt));
  89.   if err <> 0 then
  90.   begin
  91.     WriteLn('fpsetsockopt failed with ', socketerror);
  92.     Halt;
  93.   end;
  94.  
  95.   group.imr_interface.s_addr:=StrToNetAddr(nicstr).s_addr;
  96.   group.imr_multicast.s_addr:=StrToNetAddr(mcaststr).s_addr;
  97.  
  98.   {$ifdef MSWINDOWS}
  99.   err := fpsetsockopt(sock, IPPROTO_IP, 12, @group, SizeOf(group));
  100.   {$else}
  101.   err := fpsetsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
  102.          @group, SizeOf(group));
  103.   {$endif}
  104.  
  105.   if err <> 0 then
  106.   begin
  107.     WriteLn('fpsetsockopt failed with ', socketerror);
  108.     Halt;
  109.   end;
  110.  
  111.   while True do
  112.   begin
  113.        buflen := SizeOf(sender);
  114.        nbytes := fprecvfrom(sock, @buf, 4096, 0, @sender, @buflen);
  115.        if nbytes < 0 then
  116.        begin
  117.          WriteLn('fprecvfrom failed with ', socketerror);
  118.          Halt;
  119.        end;
  120.  
  121.        SetString(msg, PAnsiChar(@buf[1]), nbytes);
  122.  
  123.        WriteLn(Format('[%s@%d]: %s', [NetAddrToStr(sender.sin_addr),
  124.                                 ntohs(sender.sin_port),
  125.                                 msg]));
  126.   end;
  127. end.
  128.  
Title: Re: Multicasting on Windows
Post by: julkas on August 21, 2019, 12:09:19 pm
Hello, I was having difficulty getting multicasting to work in Windows.

I found out that somewhere along the line Microsoft changed the values of the constants used with the IPPROTO_IP flag when you use the setsockopt function. The Winsock packages for FPC have the value for IP_ADD_MEMBERSHIP set to 5, but in my version of the Windows SDK at least, this value is now 12.
Multicasting is great. I have cloned disk images (~200) with CloneZilla server / Fog.
Please provide your FPC/Lazarus/OS version.
Title: Re: Multicasting on Windows
Post by: ken_ramsey on August 21, 2019, 04:15:12 pm
Right, I should have included that, my bad:

FPC Version: 3.0.4 [2017/10/06]
Lazarus: 1.8.4
Windows Version: Windows 10 Pro
Windows SDK: 10.0.17763.0

Title: Re: Multicasting on Windows
Post by: Remy Lebeau on August 22, 2019, 12:26:56 am
I found out that somewhere along the line Microsoft changed the values of the constants used with the IPPROTO_IP flag when you use the setsockopt function. The Winsock packages for FPC have the value for IP_ADD_MEMBERSHIP set to 5, but in my version of the Windows SDK at least, this value is now 12.

Known issue with the Winsock SDK:

INFO: Header and Library Requirement When Set/Get Socket Options at the IPPROTO_IP Level (https://support.microsoft.com/en-us/help/257460/)

For comparison, Indy dynamically loads WS2.DLL on Windows CE and WS2_32.DLL on non-CE, so it uses the older values for Windows CE and the newer values for non-CE.
Title: Re: Multicasting on Windows
Post by: julkas on August 22, 2019, 10:24:57 am
Using
Code: Pascal  [Select][+][-]
  1. err := fpsetsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, @group, SizeOf(group));
returns error - fpsetsockopt failed with 10042 on my FPC 3.0.4 / Lazarus 2.0.0 / Win 8.
As described in link above.

Title: Re: Multicasting on Windows
Post by: julkas on August 22, 2019, 02:17:42 pm
I have created simple project with Indy 10 IdIPMCastServer and tested it with your multicast listener on same PC. It works.
P.S. My first impression about Indy 10 - good software.
Check multicast server and client.
Title: Re: Multicasting on Windows
Post by: Remy Lebeau on August 23, 2019, 10:12:03 pm
Using
Code: Pascal  [Select][+][-]
  1. err := fpsetsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, @group, SizeOf(group));
returns error - fpsetsockopt failed with 10042 on my FPC 3.0.4 / Lazarus 2.0.0 / Win 8.
As described in link above.

10042 is WSAENOPROTOOPT, which would be consistent with IP_ADD_MEMBERSHIP using the wrong numeric value for the version of Winsock you are accessing, as described in the MSDN article I linked to in my previous reply.

I have created simple project with Indy 10 IdIPMCastServer and tested it with your multicast listener on same PC. It works.

That is because Indy does not rely on FPC's socket constants, Indy defines its own constants.
Title: Re: Multicasting on Windows
Post by: ken_ramsey on August 25, 2019, 07:51:25 am
I have created simple project with Indy 10 IdIPMCastServer and tested it with your multicast listener on same PC. It works.
P.S. My first impression about Indy 10 - good software.
Check multicast server and client.

Yes, you are correct, Indy is good software. It is, however, focused on TCP based applications. And that is quite understandable, that is where the lions' share of interest lies. Indy is not so good with UDP multicast. I don't blame them.

My particular sphere of interest does heavily involve multicast UDP, including Zeroconf and mDNS and also much more in the UDP realm.

I've run into a multitude of problems getting Indy to work in these domains. Again, I'm not surprised, I know that I'm working in an area that isn't interesting to most.

One of my challenges is Zeroconf and mDNS, that heavily weds me to UDP multicasting solutions. But that's not all, I'm also involved in GigE Vision which also heavily weds me to UDP multicasting solutions.

For the Zeroconf and mDNS, most people use Bonjour. But many others cannot because of licensing restrictions.

Zeroconf is a wonderful thing.

If I succeed in making a good Pascal solution, I'll share it.
Title: Re: Multicasting on Windows
Post by: julkas on August 25, 2019, 10:29:54 am
I've run into a multitude of problems getting Indy to work in these domains.
Can you give more info about?
One of my challenges is Zeroconf and mDNS, that heavily weds me to UDP multicasting solutions. But that's not all, I'm also involved in GigE Vision which also heavily weds me to UDP multicasting solutions.

For the Zeroconf and mDNS, most people use Bonjour. But many others cannot because of licensing restrictions.

Zeroconf is a wonderful thing.

If I succeed in making a good Pascal solution, I'll share it.
Very interesting projects.
Regards.
Title: Re: Multicasting on Windows
Post by: Remy Lebeau on August 27, 2019, 07:42:30 pm
I've run into a multitude of problems getting Indy to work in these domains.
Can you give more info about?

Yes, please.  I can't help you if you don't explain what is actually wrong.  Also, Indy does have an issue tracker (https://github.com/IndySockets/Indy/issues) you can submit problems to.
TinyPortal © 2005-2018