Recent

Author Topic: Easiest way to send UDP packets  (Read 2351 times)

ChrisTG

  • New Member
  • *
  • Posts: 15
Easiest way to send UDP packets
« on: May 27, 2024, 11:57:50 am »
Hi there,
I'm looking for an easy approach to send out UDP packets from an Windows application.

First I tried to get ICS9.1 working, but I didn't manage to import it into Lazarus. Seems like the file-extensions in the "Installer" dir are not compatible with Lazarus.

Then I saw that there's a ready to install version of Indy10 in the Online package installer. So I installed it.
What I'm now missing, is a hint about "how to start". The Indy-site and various resources seem to be offline meanwhile.
Has anyone a hint for me? Thanks!

BSaidus

  • Hero Member
  • *****
  • Posts: 560
  • lazarus 1.8.4 Win8.1 / cross FreeBSD
Re: Easiest way to send UDP packets
« Reply #1 on: May 27, 2024, 01:33:13 pm »
Hello
Indy sockets with demos, you can find in github : https://github.com/indysockets.
You can search the internet to find tutos.

You can look at this page for downloading chm manual : https://github.com/IndySockets/Indy/issues/337

lazarus 1.8.4 Win8.1 / cross FreeBSD
dhukmucmur vernadh!

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Easiest way to send UDP packets
« Reply #2 on: May 27, 2024, 01:45:18 pm »
Marco van de Voort wrote there a tutorial how-to modify ICS to work with FPC... back in 2005 :D
Since I never tested it with FPC (FPC got already a lot of goodies included) I can't tell if its still like that.
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Warfley

  • Hero Member
  • *****
  • Posts: 1511
Re: Easiest way to send UDP packets
« Reply #3 on: May 27, 2024, 03:28:04 pm »
Berkley Sockets in unit Sockets. It's a C API so a bit cumbersome, so I've written a small wrapper using some FPC native types (like strings, records, etc.): https://github.com/Warfley/PasSimpleSockets

See examples/udpclient:
Code: Pascal  [Select][+][-]
  1. program udpclient;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   SimpleSockets;
  7.  
  8. var
  9.   Sock: TSocket;
  10.   Msg: TReceiveFromStringMessage;
  11. begin
  12.   Sock := UDPSocket(stIPv4);
  13.   try
  14.     SendStrTo(Sock, '127.0.0.1', 1337, 'Hello UDP');
  15.     Msg := ReceiveStrFrom(Sock);
  16.     WriteLn('Server at ', Msg.FromAddr.Address, ':', Msg.FromPort, ' answered: ', Msg.Data);
  17.   finally
  18.     CloseSocket(Sock);
  19.   end;
  20.   ReadLn;
  21. end.

Curt Carpenter

  • Sr. Member
  • ****
  • Posts: 417
Re: Easiest way to send UDP packets
« Reply #4 on: May 27, 2024, 10:00:38 pm »
I use the TLUDP component from LNet (OPM).

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11605
  • FPC developer.
Re: Easiest way to send UDP packets
« Reply #5 on: May 27, 2024, 10:18:29 pm »
I just use Indy. Back then ICS was the only one with non-VCL demos, but then they decided to put SSL behind a paywall and I converted to Indy10.

Thaddy

  • Hero Member
  • *****
  • Posts: 14850
  • Censorship about opinions does not belong here.
Re: Easiest way to send UDP packets
« Reply #6 on: May 28, 2024, 10:41:03 am »
I never use components for that, I use either fcl-net or synapse.
Warfley's code looks interesting, but I can't get it to work. (probably impatience on my side)
Remember the Medway disaster..

MarkMLl

  • Hero Member
  • *****
  • Posts: 6943
Re: Easiest way to send UDP packets
« Reply #7 on: May 28, 2024, 10:56:36 am »
I don't like getting involved with this since I'm a unix rather than Windows user, but for the specific case of UDP I'd agree with Warfley and suggest the standard Sockets unit.

I would not give that advice if a connection-oriented protocol which could benefit from SSL were involved, or for a higher-level protocol such as DNS lookups or HTTP.

I'd specifically suggest avoiding the LNet library, since my experience is that debugging it when it goes wrong is an utter so^Hwine.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 14850
  • Censorship about opinions does not belong here.
Re: Easiest way to send UDP packets
« Reply #8 on: May 28, 2024, 11:43:12 am »
I agree with your lnet remark, but your examples do not work and I am on linux too.
I am sure it is just a misunderstanding on my side.
Remember the Medway disaster..

MarkMLl

  • Hero Member
  • *****
  • Posts: 6943
Re: Easiest way to send UDP packets
« Reply #9 on: May 28, 2024, 03:17:47 pm »
I agree with your lnet remark, but your examples do not work and I am on linux too.
I am sure it is just a misunderstanding on my side.

Correct. You didn't check who'd provided the example.

I've just checked and I don't have anything convenient to hand, i.e. with socket creation and message sending next to each other. However one of the advantages of using the traditional sockets unit is that it is basically the standard Berkeley API (of which Winsock is a derivative), so as well as the documentation at https://www.freepascal.org/docs-html/current/rtl/sockets/index.html being relevant there is a vast amount of information floating around the Internet: detailed description of the various socket options and so on.

As soon as you move away from the standard API, you risk cutting yourself off from the associated doctrine and accumulated knowledge. And that applies to both using clever components or libraries, and to using books like Comer & Stevens which try to embellish the standard routines.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1343
    • Lebeau Software
Re: Easiest way to send UDP packets
« Reply #10 on: May 28, 2024, 08:34:22 pm »
Then I saw that there's a ready to install version of Indy10 in the Online package installer. So I installed it.
What I'm now missing, is a hint about "how to start".

Start with the TIdUDPClient component.  Set the Host and Port properties to the desired target, then call the SendBuffer() method as needed.

The Indy-site and various resources seem to be offline meanwhile.

The site is not offline, but portions of it are broken:

https://www.indyproject.org/2021/02/10/links-to-old-indy-website-pages-are-currently-broken/
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

ChrisTG

  • New Member
  • *
  • Posts: 15
Re: Easiest way to send UDP packets
« Reply #11 on: May 28, 2024, 09:06:03 pm »
Hi guys,
thank you all for your helpful replies.
I think I will start with the neat little unit Warfley created. It looks quite similar to my try. I tried to use "Winsock2" directly since there are a few C++ examples for sending UDP broadcasts in the net. My goal is to send a WOL packet. But apparently it does not work:

Code: Pascal  [Select][+][-]
  1. program udp_send;
  2.  
  3. uses winsock2, sysutils, strutils, windows;
  4.  
  5. function inet_pton(Family: Integer; pszAddrString: PAnsiChar; pAddrBuf: Pointer): Integer; stdcall; external 'Ws2_32.dll';
  6.  
  7. var
  8.   Sock: TSocket;
  9.   Addr: TSockAddr;
  10.   LocalAddr: TSockAddr;
  11.   LocalIP: PChar = '0.0.0.0';
  12.   HostIP: PChar = '192.168.1.255';
  13.   HostPort: Word = 7;
  14.   AString: String = chr(255) + chr(255) + chr(255) + chr(255) + chr(255) + chr(255);
  15.   MAC: String = '112233445566';
  16.   MACchr: String;
  17.   i: Integer;
  18.   WSData: TWSAData;
  19.   Optval: dword;
  20.  
  21. begin
  22.   i := 1;
  23.   MACchr := '';
  24.   while i < Length(MAC) do
  25.   begin
  26.     MACchr := MACchr + chr(StrToInt('$'+MAC[i]+MAC[i+1]));
  27.     i := i + 2;
  28.   end;
  29.  
  30.   MACchr := DupeString(MACchr, 16);
  31.   AString := AString + MACchr;
  32.  
  33.   WSdata := Default(TWSAData);
  34.   WSAStartup(makeword(2,2), WSDATA);
  35.  
  36.     LocalAddr.sin_family := AF_INET;
  37.     LocalAddr.sin_port := htons(HostPort);
  38.     inet_pton(AF_INET, LocalIP, @LocalAddr.sin_addr.s_addr);
  39.  
  40.     Addr.sin_family := AF_INET;
  41.     Addr.sin_port := htons(HostPort);
  42.     inet_pton(AF_INET, HostIP, @Addr.sin_addr.s_addr);
  43.  
  44.     Sock := socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  45.  
  46.     Optval := 1;
  47.     setsockopt(Sock, SOL_SOCKET, SO_BROADCAST, Optval, sizeof(Optval));
  48.  
  49.     bind(Sock, LocalAddr, SizeOf(LocalAddr));
  50.     SendTo(Sock, AString, Length(AString), 0, Addr, SizeOf(Addr));
  51.  
  52.     CloseSocket(Sock);
  53.     WSACleanup;
  54.  
  55. end.
  56.  
Note that I defined the function inet_pton here directly as it is missing in FP's "Winsock2" unit somehow.
I found that one has to use "setsockopt" to be able to send out on broadcast-addresses, but still the broadcasts are not sent.
What am I doing wrong?
« Last Edit: May 28, 2024, 09:32:30 pm by ChrisTG »

TRon

  • Hero Member
  • *****
  • Posts: 2801
Re: Easiest way to send UDP packets
« Reply #12 on: May 29, 2024, 03:31:35 am »
I think I will start with the neat little unit Warfley created. It looks quite similar to my try. I tried to use "Winsock2" directly since there are a few C++ examples for sending UDP broadcasts in the net.
Why the complicated and dependent winsock usage while Free Pascal has a (platform agnostic) sockets units ?

Quote
But apparently it does not work:
It is not so apparent because the presented code fails to show/provide any error.

Code: Pascal  [Select][+][-]
  1. program clumsyexample;
  2.  
  3. {$mode objfpc}{$h+}
  4.  
  5. uses
  6.   sockets, sysutils;
  7.  
  8. procedure Inform(condition: boolean; trues, falses: string);
  9. var
  10.   errcode: integer;
  11.   errstr : string;
  12. begin
  13.   if condition then writeln(trues) else
  14.   begin
  15.     errcode := GetLastOSError;
  16.     errstr  := SysErrorMessage(errcode);
  17.     writeln('ERROR: ', falses, ' (error code ', errcode, ' = ', errstr,')');
  18.   end;
  19. end;
  20.  
  21. procedure client;
  22. var
  23.   sock       : TSocket;
  24.   optval     : int32 = 1;
  25.  
  26.   remotipstr : string = '127.0.0.1';
  27.   remotport  : integer = 8888;
  28.   remotaddr  : TSockAddr;
  29.  
  30.   msg        : pchar = 'hello world';
  31.   retval     : integer;
  32. begin
  33.   remotaddr.sin_family := AF_INET;
  34.   remotaddr.sin_port := htons(remotport);
  35.   remotaddr.sin_addr := StrToNetAddr(remotipstr);
  36.  
  37.   sock := fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  38.   inform(sock <> -1, 'new socket created', 'unable to create new socket');
  39.  
  40.   retval := fpsetsockopt(sock, SOL_SOCKET, SO_BROADCAST, @optval, sizeof(optval));
  41.   inform(retval <> -1, 'socket option set', 'unable to set socket option');
  42.  
  43.   retval := fpsendto(sock, msg, length(msg), 0, @remotaddr, sizeof(remotaddr));
  44.   inform(retval <> -1, 'message broadcasted', 'unable to broadcast message');
  45.  
  46.   retval := closesocket(sock);
  47.   inform(retval <> -1, 'socket closed', 'unable to close socket');
  48. end;
  49.  
  50. procedure server;
  51. var
  52.   sock        : TSocket;
  53.  
  54.   localaddr   : TSockAddr;
  55.   localport   : integer = 8888;
  56.   remotaddr   : TSockAddr;
  57.   len         : integer;
  58.   retval      : integer;
  59.   slen        : TSockLen;
  60.  
  61.   buf : array[0..200] of char;
  62. begin
  63.   localaddr.sin_family      := AF_INET;
  64.   localaddr.sin_port        := htons(localport);
  65.   localaddr.sin_addr.s_addr := htonl(INADDR_ANY);
  66.  
  67.   sock := fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  68.   inform(sock <> -1, 'new socket created', 'unable to create new socket');
  69.  
  70.   retval := fpbind(sock, @localaddr, sizeof(localaddr));
  71.   inform(retval <> -1, 'bind succeeded', 'unable to bind socket');
  72.  
  73.   while true do
  74.   begin
  75.     writeln('waiting for data...');
  76.     len := fprecvfrom(sock, @buf[0], sizeof(buf), 0, @remotaddr, @slen);
  77.     inform(len <> -1, 'message received', 'unable to receive message');
  78.     if len <> -1 then
  79.     begin
  80.       writeln
  81.       (
  82.         format('received message "%s" from %s:%d',
  83.         [copy(buf, 0, len), NetAddrToStr(remotaddr.sin_addr), ntohs(remotaddr.sin_port)])
  84.       );
  85.     end;
  86.   end;
  87.  
  88.   retval := closesocket(sock);
  89.   inform(retval <> -1, 'socket closed', 'unable to close socket');
  90. end;
  91.  
  92. begin
  93.   if (paramcount = 1) and (paramstr(1) = 'client') then
  94.   begin
  95.     writeln('start client');
  96.     client;
  97.   end
  98.   else
  99.   begin
  100.     writeln('start server');
  101.     server;
  102.   end;
  103. end.
  104.  

Windows firewall, restricted ports etc.... just to name a few things.

dseligo

  • Hero Member
  • *****
  • Posts: 1261
Re: Easiest way to send UDP packets
« Reply #13 on: May 29, 2024, 10:47:56 am »
I think I will start with the neat little unit Warfley created. It looks quite similar to my try. I tried to use "Winsock2" directly since there are a few C++ examples for sending UDP broadcasts in the net.
Why the complicated and dependent winsock usage while Free Pascal has a (platform agnostic) sockets units ?

Quote
But apparently it does not work:
It is not so apparent because the presented code fails to show/provide any error.

You example gives error:
Code: Text  [Select][+][-]
  1. ERROR: unable to receive message (error code 10014 = The system detected an invalid pointer address in attempting to use a pointer argument in a call.)

You didn't set length of remoteaddr in slen.

This works (I only added line 73):

Code: Pascal  [Select][+][-]
  1. program clumsyexample;
  2.  
  3. {$mode objfpc}{$h+}
  4.  
  5. uses
  6.   sockets, sysutils;
  7.  
  8. procedure Inform(condition: boolean; trues, falses: string);
  9. var
  10.   errcode: integer;
  11.   errstr : string;
  12. begin
  13.   if condition then writeln(trues) else
  14.   begin
  15.     errcode := GetLastOSError;
  16.     errstr  := SysErrorMessage(errcode);
  17.     writeln('ERROR: ', falses, ' (error code ', errcode, ' = ', errstr,')');
  18.   end;
  19. end;
  20.  
  21. procedure client;
  22. var
  23.   sock       : TSocket;
  24.   optval     : int32 = 1;
  25.  
  26.   remotipstr : string = '127.0.0.1';
  27.   remotport  : integer = 8888;
  28.   remotaddr  : TSockAddr;
  29.  
  30.   msg        : pchar = 'hello world';
  31.   retval     : integer;
  32. begin
  33.   remotaddr.sin_family := AF_INET;
  34.   remotaddr.sin_port := htons(remotport);
  35.   remotaddr.sin_addr := StrToNetAddr(remotipstr);
  36.  
  37.   sock := fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  38.   inform(sock <> -1, 'new socket created', 'unable to create new socket');
  39.  
  40.   retval := fpsetsockopt(sock, SOL_SOCKET, SO_BROADCAST, @optval, sizeof(optval));
  41.   inform(retval <> -1, 'socket option set', 'unable to set socket option');
  42.  
  43.   retval := fpsendto(sock, msg, length(msg), 0, @remotaddr, sizeof(remotaddr));
  44.   inform(retval <> -1, 'message broadcasted', 'unable to broadcast message');
  45.  
  46.   retval := closesocket(sock);
  47.   inform(retval <> -1, 'socket closed', 'unable to close socket');
  48. end;
  49.  
  50. procedure server;
  51. var
  52.   sock        : TSocket;
  53.  
  54.   localaddr   : TSockAddr;
  55.   localport   : integer = 8888;
  56.   remotaddr   : TSockAddr;
  57.   len         : integer;
  58.   retval      : integer;
  59.   slen        : TSockLen;
  60.  
  61.   buf : array[0..200] of char;
  62. begin
  63.   localaddr.sin_family      := AF_INET;
  64.   localaddr.sin_port        := htons(localport);
  65.   localaddr.sin_addr.s_addr := htonl(INADDR_ANY);
  66.  
  67.   sock := fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  68.   inform(sock <> -1, 'new socket created', 'unable to create new socket');
  69.  
  70.   retval := fpbind(sock, @localaddr, sizeof(localaddr));
  71.   inform(retval <> -1, 'bind succeeded', 'unable to bind socket');
  72.  
  73.   slen := SizeOf(remotaddr);
  74.  
  75.   while true do
  76.   begin
  77.     writeln('waiting for data...');
  78.     len := fprecvfrom(sock, @buf[0], sizeof(buf), 0, @remotaddr, @slen);
  79.     inform(len <> -1, 'message received', 'unable to receive message');
  80.     if len <> -1 then
  81.     begin
  82.       writeln
  83.       (
  84.         format('received message "%s" from %s:%d',
  85.         [copy(buf, 0, len), NetAddrToStr(remotaddr.sin_addr), ntohs(remotaddr.sin_port)])
  86.       );
  87.     end;
  88.   end;
  89.  
  90.   retval := closesocket(sock);
  91.   inform(retval <> -1, 'socket closed', 'unable to close socket');
  92. end;
  93.  
  94. begin
  95.   if (paramcount = 1) and (paramstr(1) = 'client') then
  96.   begin
  97.     writeln('start client');
  98.     client;
  99.   end
  100.   else
  101.   begin
  102.     writeln('start server');
  103.     server;
  104.   end;
  105. end.
  106.  

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11605
  • FPC developer.
Re: Easiest way to send UDP packets
« Reply #14 on: May 29, 2024, 10:54:24 am »
I never use components for that, I use either fcl-net or synapse.

I use Indy10 in threads, so no designtime either.  I stuck with using libraris rather than rolling my own because of the asynchronous nature. Most basic socket examples are synchronous/blocking I/O.

And anyway, Linux/FreeBSD is only of secondary importance for me.

 

TinyPortal © 2005-2018