Easiest way to send UDP packets lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- ChrisTG · 2024-05-27 09:57 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 · 2024-05-27 11:33 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 ---------------------------------------------------------------------- KodeZwerg · 2024-05-27 11:45 Marco van de Voort wrote there a tutorial how-to modify ICS to work with FPC... back in 2005 Since I never tested it with FPC (FPC got already a lot of goodies included) I can't tell if its still like that. ---------------------------------------------------------------------- Warfley · 2024-05-27 13:28 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program udpclient; {$mode objfpc}{$H+} uses  SimpleSockets; var  Sock: TSocket;  Msg: TReceiveFromStringMessage;begin  Sock := UDPSocket(stIPv4);  try    SendStrTo(Sock, '127.0.0.1', 1337, 'Hello UDP');    Msg := ReceiveStrFrom(Sock);    WriteLn('Server at ', Msg.FromAddr.Address, ':', Msg.FromPort, ' answered: ', Msg.Data);  finally    CloseSocket(Sock);  end;  ReadLn;end. ---------------------------------------------------------------------- Curt Carpenter · 2024-05-27 20:00 I use the TLUDP component from LNet (OPM). ---------------------------------------------------------------------- marcov · 2024-05-27 20:18 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 · 2024-05-28 08:41 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) ---------------------------------------------------------------------- MarkMLl · 2024-05-28 08:56 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 ---------------------------------------------------------------------- Thaddy · 2024-05-28 09:43 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. ---------------------------------------------------------------------- MarkMLl · 2024-05-28 13:17 Quote from: Thaddy on May 28, 2024, 11:43:12 amI 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 ---------------------------------------------------------------------- Remy Lebeau · 2024-05-28 18:34 Quote from: ChrisTG on May 27, 2024, 11:57:50 amThen 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. Quote from: ChrisTG on May 27, 2024, 11:57:50 amThe 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/ ---------------------------------------------------------------------- ChrisTG · 2024-05-28 19:06 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program udp_send; uses winsock2, sysutils, strutils, windows; function inet_pton(Family: Integer; pszAddrString: PAnsiChar; pAddrBuf: Pointer): Integer; stdcall; external 'Ws2_32.dll'; var  Sock: TSocket;  Addr: TSockAddr;  LocalAddr: TSockAddr;  LocalIP: PChar = '0.0.0.0';  HostIP: PChar = '192.168.1.255';  HostPort: Word = 7;  AString: String = chr(255) + chr(255) + chr(255) + chr(255) + chr(255) + chr(255);  MAC: String = '112233445566';  MACchr: String;  i: Integer;  WSData: TWSAData;  Optval: dword; begin  i := 1;  MACchr := '';  while i < Length(MAC) do  begin    MACchr := MACchr + chr(StrToInt('$'+MAC[i]+MAC[i+1]));    i := i + 2;  end;   MACchr := DupeString(MACchr, 16);  AString := AString + MACchr;   WSdata := Default(TWSAData);  WSAStartup(makeword(2,2), WSDATA);     LocalAddr.sin_family := AF_INET;    LocalAddr.sin_port := htons(HostPort);    inet_pton(AF_INET, LocalIP, @LocalAddr.sin_addr.s_addr);     Addr.sin_family := AF_INET;    Addr.sin_port := htons(HostPort);    inet_pton(AF_INET, HostIP, @Addr.sin_addr.s_addr);     Sock := socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);     Optval := 1;    setsockopt(Sock, SOL_SOCKET, SO_BROADCAST, Optval, sizeof(Optval));     bind(Sock, LocalAddr, SizeOf(LocalAddr));    SendTo(Sock, AString, Length(AString), 0, Addr, SizeOf(Addr));     CloseSocket(Sock);    WSACleanup; end. 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? ---------------------------------------------------------------------- TRon · 2024-05-29 01:31 Quote from: ChrisTG on May 28, 2024, 09:06:03 pmI 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 ? QuoteBut apparently it does not work: It is not so apparent because the presented code fails to show/provide any error. 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 program clumsyexample; {$mode objfpc}{$h+} uses  sockets, sysutils; procedure Inform(condition: boolean; trues, falses: string);var  errcode: integer;  errstr : string;begin  if condition then writeln(trues) else  begin    errcode := GetLastOSError;    errstr  := SysErrorMessage(errcode);    writeln('ERROR: ', falses, ' (error code ', errcode, ' = ', errstr,')');  end;end; procedure client;var  sock     : TSocket;  optval     : int32 = 1;   remotipstr : string = '127.0.0.1';  remotport  : integer = 8888;  remotaddr  : TSockAddr;   msg        : pchar = 'hello world';  retval     : integer;begin  remotaddr.sin_family := AF_INET;  remotaddr.sin_port := htons(remotport);  remotaddr.sin_addr := StrToNetAddr(remotipstr);   sock := fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  inform(sock <> -1, 'new socket created', 'unable to create new socket');   retval := fpsetsockopt(sock, SOL_SOCKET, SO_BROADCAST, @optval, sizeof(optval));  inform(retval <> -1, 'socket option set', 'unable to set socket option');   retval := fpsendto(sock, msg, length(msg), 0, @remotaddr, sizeof(remotaddr));  inform(retval <> -1, 'message broadcasted', 'unable to broadcast message');   retval := closesocket(sock);  inform(retval <> -1, 'socket closed', 'unable to close socket');end; procedure server;var  sock        : TSocket;   localaddr   : TSockAddr;  localport   : integer = 8888;  remotaddr   : TSockAddr;  len         : integer;  retval      : integer;  slen      : TSockLen;   buf : array[0..200] of char;begin  localaddr.sin_family      := AF_INET;  localaddr.sin_port        := htons(localport);  localaddr.sin_addr.s_addr := htonl(INADDR_ANY);   sock := fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  inform(sock <> -1, 'new socket created', 'unable to create new socket');   retval := fpbind(sock, @localaddr, sizeof(localaddr));  inform(retval <> -1, 'bind succeeded', 'unable to bind socket');   while true do  begin    writeln('waiting for data...');    len := fprecvfrom(sock, @buf[0], sizeof(buf), 0, @remotaddr, @slen);    inform(len <> -1, 'message received', 'unable to receive message');    if len <> -1 then    begin      writeln      (        format('received message "%s" from %s:%d',        [copy(buf, 0, len), NetAddrToStr(remotaddr.sin_addr), ntohs(remotaddr.sin_port)])      );    end;  end;   retval := closesocket(sock);  inform(retval <> -1, 'socket closed', 'unable to close socket');end; begin  if (paramcount = 1) and (paramstr(1) = 'client') then  begin    writeln('start client');    client;  end  else  begin    writeln('start server');    server;  end;end.  Windows firewall, restricted ports etc.... just to name a few things. ---------------------------------------------------------------------- dseligo · 2024-05-29 08:47 Quote from: TRon on May 29, 2024, 03:31:35 amQuote from: ChrisTG on May 28, 2024, 09:06:03 pmI 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 ? QuoteBut 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program clumsyexample; {$mode objfpc}{$h+} uses  sockets, sysutils; procedure Inform(condition: boolean; trues, falses: string);var  errcode: integer;  errstr : string;begin  if condition then writeln(trues) else  begin    errcode := GetLastOSError;    errstr  := SysErrorMessage(errcode);    writeln('ERROR: ', falses, ' (error code ', errcode, ' = ', errstr,')');  end;end; procedure client;var  sock     : TSocket;  optval     : int32 = 1;   remotipstr : string = '127.0.0.1';  remotport  : integer = 8888;  remotaddr  : TSockAddr;   msg        : pchar = 'hello world';  retval     : integer;begin  remotaddr.sin_family := AF_INET;  remotaddr.sin_port := htons(remotport);  remotaddr.sin_addr := StrToNetAddr(remotipstr);   sock := fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  inform(sock <> -1, 'new socket created', 'unable to create new socket');   retval := fpsetsockopt(sock, SOL_SOCKET, SO_BROADCAST, @optval, sizeof(optval));  inform(retval <> -1, 'socket option set', 'unable to set socket option');   retval := fpsendto(sock, msg, length(msg), 0, @remotaddr, sizeof(remotaddr));  inform(retval <> -1, 'message broadcasted', 'unable to broadcast message');   retval := closesocket(sock);  inform(retval <> -1, 'socket closed', 'unable to close socket');end; procedure server;var  sock        : TSocket;   localaddr   : TSockAddr;  localport   : integer = 8888;  remotaddr   : TSockAddr;  len         : integer;  retval      : integer;  slen      : TSockLen;   buf : array[0..200] of char;begin  localaddr.sin_family      := AF_INET;  localaddr.sin_port        := htons(localport);  localaddr.sin_addr.s_addr := htonl(INADDR_ANY);   sock := fpsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  inform(sock <> -1, 'new socket created', 'unable to create new socket');   retval := fpbind(sock, @localaddr, sizeof(localaddr));  inform(retval <> -1, 'bind succeeded', 'unable to bind socket');   slen := SizeOf(remotaddr);   while true do  begin    writeln('waiting for data...');    len := fprecvfrom(sock, @buf[0], sizeof(buf), 0, @remotaddr, @slen);    inform(len <> -1, 'message received', 'unable to receive message');    if len <> -1 then    begin      writeln      (        format('received message "%s" from %s:%d',        [copy(buf, 0, len), NetAddrToStr(remotaddr.sin_addr), ntohs(remotaddr.sin_port)])      );    end;  end;   retval := closesocket(sock);  inform(retval <> -1, 'socket closed', 'unable to close socket');end; begin  if (paramcount = 1) and (paramstr(1) = 'client') then  begin    writeln('start client');    client;  end  else  begin    writeln('start server');    server;  end;end. ---------------------------------------------------------------------- marcov · 2024-05-29 08:54 Quote from: Thaddy on May 28, 2024, 10:41:03 amI 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. ---------------------------------------------------------------------- TRon · 2024-05-29 08:59 Quote from: dseligo on May 29, 2024, 10:47:56 amYou example gives error: Thank you very much for testing and the feedback dseligo. That is strange... according to the fpc documentation it should retrieve the length. QuotefpRecvFrom receives data in buffer Buf with maximum length Len from socket S. Receipt is controlled by options in Flags. The location pointed to by from will be filled with the address from the sender, and it's length will be stored in fromlen. The function returns the number of bytes received, or -1 on error. AddrLen. So, it is/was my understanding I did not have to set slen ... but perhaps my interpretation of the bold underlined text is wrong ? edit: Indeed according to man: QuoteThe argument addrlen is a value-result argument, which the caller should initialize before the call to the size of the buffer associated with src_addr, and modified on return to indicate the actual size of the source address. So, I stand corrected. And also a good point to show that checking results is important  (though strangely the original code worked without errors for me on linux 64 bit) ---------------------------------------------------------------------- ChrisTG · 2024-06-03 18:31 A small update from my side: I was able to succefully generate and send the WOL-packet using Warfley's neat unit. Here's my updated code: 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 program udp_send; {$mode objfpc}{$H+} uses SimpleSockets, sysutils, strutils; var  Sock: TSocket;  HostIP: PChar = '192.168.1.255';  HostPort: Word = 7;  AString: String = chr(255) + chr(255) + chr(255) + chr(255) + chr(255) + chr(255);  MAC: String = '112233445566';  MACchr: String;  i: Integer; begin  i := 1;  MACchr := '';  while i < Length(MAC) do  begin    MACchr := MACchr + chr(StrToInt('$'+MAC[i]+MAC[i+1]));    i := i + 2;  end;   MACchr := DupeString(MACchr, 16);  AString := AString + MACchr;   Sock := UDPSocket(stIPv4);  try    SendStrTo(Sock, IN4Address(HostIP), HostPort, AString);  finally    CloseSocket(Sock);  end; end.           Two points are interesting here: 1) Somehow no socketopts are needed for sending broadcast-packets, even though there is no code to setting this in Warfley's unit. 2) My native approach using the "Sockets"-unit did not work, but this wrapper-unit does. So there must be some magic in Warfley's code somewhere. I think I'll dig through it to understand the mistake(s) in my former tries. Anyway, thank you all for your input here, especially Warfley for creating this useful unit. ---------------------------------------------------------------------- MarkMLl · 2024-06-03 20:56 Quote from: ChrisTG on June 03, 2024, 08:31:29 pm1) Somehow no socketopts are needed for sending broadcast-packets, even though there is no code to setting this in Warfley's unit. From memory, I don't think Windows is as strict at rejecting this sort of thing. However even the unix approach is, by now, missing an important point: for at least the last 20 years it has been relatively easy for a computer's privileged user to get it connected directly to the Internet, i.e. without one or more NAT layers. To assume that every one of those billion or so users is a "responsible netizen" is unrealistic, and sooner or later we are going to see fundamental protocol changes to accommodate that. It is unlikely that those changes will leave network programming anything like as easy as it has been for the last 40+ years. Quote2) My native approach using the "Sockets"-unit did not work, but this wrapper-unit does. So there must be some magic in Warfley's code somewhere. I think I'll dig through it to understand the mistake(s) in my former tries. If I had a Windows- and preferably a WoL target- system conveniently to hand I'd take a look at that. MarkMLl