Recent

Author Topic: cannot receive messages; udp server with lnet  (Read 13512 times)

prodingus

  • Jr. Member
  • **
  • Posts: 83
Re: cannot receive messages; udp server with lnet
« Reply #15 on: July 02, 2022, 04:40:22 pm »
Quote
Considering that you're using lnet you are using Object Pascal. So if you want to use the components correctly you must use the mechanisms and concepts provided by Object Pascal (which includes event handlers).

Can these handlers be used in my example without creating classes?
lazarus 2.2.0, FPC 3.2.2

PascalDragon

  • Hero Member
  • *****
  • Posts: 5448
  • Compiler Developer
Re: cannot receive messages; udp server with lnet
« Reply #16 on: July 02, 2022, 04:42:31 pm »
Quote
Considering that you're using lnet you are using Object Pascal. So if you want to use the components correctly you must use the mechanisms and concepts provided by Object Pascal (which includes event handlers).

Can these handlers be used in my example without creating classes?

With the development version of FPC (3.3.1) you could use anonymous functions for that, but otherwise and without opening a rabbit hole that might lead to suffering and pain in the future: no.

rvk

  • Hero Member
  • *****
  • Posts: 6112
Re: cannot receive messages; udp server with lnet
« Reply #17 on: July 02, 2022, 05:11:17 pm »
Before switching to lnet, I was testing with BlckSock (synapse?).
So why did you want to switch to LNet?

Can these handlers be used in my example without creating classes?
You can try to use a simple class with class function which handles your data. Then you wouldn't need to create an instance but can use the @TDummy.MyReceive() function directly. Not sure if that would work. I prefer Synapse

prodingus

  • Jr. Member
  • **
  • Posts: 83
Re: cannot receive messages; udp server with lnet
« Reply #18 on: July 02, 2022, 05:49:47 pm »
Quote
So why did you want to switch to LNet?
I read that lnet was asynchronus, thats why.

After reading the comments on this thread, it seems lnet is no-go for me. Shall I start a new thread about sockets?
lazarus 2.2.0, FPC 3.2.2

rvk

  • Hero Member
  • *****
  • Posts: 6112
Re: cannot receive messages; udp server with lnet
« Reply #19 on: July 02, 2022, 06:05:31 pm »
Quote
So why did you want to switch to LNet?
I read that lnet was asynchronus, thats why.
If you want asynchronous reading of sockets you will always end up with classes and/or threads. That would also be the case for Synapse because it by itself is 'blocking' (the class is even called TBlockSocket in Synapse). You can make your it asynchronous by using it in threads.

But if you want everything in your main procedure and don't want to work with classes and/or threads, you WANT to have blocking reading. So you need to think about what you want first.

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: cannot receive messages; udp server with lnet
« Reply #20 on: July 02, 2022, 07:05:49 pm »
The easiest way to receive data via UDP is simply using the base socket unit which is provided by the fpc without any additional packages:
Code: Pascal  [Select][+][-]
  1. uses
  2.   sockets;
  3.  
  4. const
  5.   UDPPackLen = 512;
  6. var
  7.   sock: Tsocket;
  8.   buff: Array[0..UDPPackLen-1] of Byte;
  9.   ServerAddr, FromAddr: sockaddr_in;
  10.   FromAddrLen: Integer;
  11.   ClientAddr: String;
  12.   ClientPort: Word;
  13.   MessageSize: SizeInt;
  14. begin
  15.   // Start UDP Server
  16.   sock := fpsocket(AF_INET, SOCK_DGRAM, 0);
  17.   ServerAddr.sin_family:= AF_INET;
  18.   ServerAddr.sin_addr := StrToNetAddr('1.1.1.2'); // server IP
  19.   ServerAddr.sin_port := 531; // server port
  20.   fpbind(sock, @ServerAddr, SizeOf(ServerAddr));
  21.   // Receive Data
  22.   MessageSize := fprecvfrom(sock, @buff, SizeOf(buff), 0, @FromAddr, @FromAddrLen);
  23.   ClientAddr := NetAddrToStr(FromAddr.sin_addr);
  24.   ClientPort := NToHs(FromAddr.sin_port);
  25. end.

Quote
So why did you want to switch to LNet?
I read that lnet was asynchronus, thats why.

After reading the comments on this thread, it seems lnet is no-go for me. Shall I start a new thread about sockets?
Use select on the socket to check if there is any data. But true asynchronous execution requires your application to be built around that. It's not something you can just "toogle on" using a library. Either you have threads that fire events or you use something like AsyncNet which builds on STAX

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: cannot receive messages; udp server with lnet
« Reply #21 on: July 02, 2022, 07:27:50 pm »
The easiest way to receive data via UDP is simply using the base socket unit which is provided by the fpc without any additional packages:

I agree. And I'm afraid that my opinion of LNet is that it's a can of worms: I've come across various pathological behaviour particularly when one end or the other of a (TCP) connection dropps unexpectedly and the convoluted nature of its code- which attempts to simulate asynchronous operation without doing it "properly" using threads- makes maintenance "tricky".

The variant of LNet bundled with FPC has a bit of extra Telnet handling that isn't in the one on Github, which makes it useful for writing a debugging backdoor for daemon programs (I stress that this should only be used locally because of Telnet's lack of encryption) or a console handler for e.g. a mainframe emulator. But if I needed that functionality for something going into the field I'd redo it from scratch.

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

prodingus

  • Jr. Member
  • **
  • Posts: 83
Re: cannot receive messages; udp server with lnet
« Reply #22 on: July 02, 2022, 08:12:25 pm »
Quote
The easiest way to receive data via UDP is simply using the base socket unit which is provided by the fpc without any additional packages

That's why I started a new thread.

Code: Pascal  [Select][+][-]
  1. uses
  2.   sockets;
  3.  
  4. const
  5.   UDPPackLen = 512;
  6. var
  7.   sock: Tsocket;
  8.   buff: Array[0..UDPPackLen-1] of Byte;
  9.   ServerAddr, FromAddr: sockaddr_in;
  10.   FromAddrLen: Integer;
  11.   ClientAddr: String;
  12.   ClientPort: Word;
  13.   MessageSize: SizeInt;
  14. begin
  15.   // Start UDP Server
  16.   sock := fpsocket(AF_INET, SOCK_DGRAM, 0);
  17.   ServerAddr.sin_family:= AF_INET;
  18.   ServerAddr.sin_addr := StrToNetAddr('1.1.1.2'); // server IP
  19.   ServerAddr.sin_port := 531; // server port
  20.   fpbind(sock, @ServerAddr, SizeOf(ServerAddr));
  21.   // Receive Data
  22.   MessageSize := fprecvfrom(sock, @buff, SizeOf(buff), 0, @FromAddr, @FromAddrLen);
  23.   ClientAddr := NetAddrToStr(FromAddr.sin_addr);
  24.   ClientPort := NToHs(FromAddr.sin_port);
  25. end.

How to writeln the buff??
lazarus 2.2.0, FPC 3.2.2

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: cannot receive messages; udp server with lnet
« Reply #23 on: July 02, 2022, 08:22:27 pm »
Put it into a string, set the length, and then proceed in the usual fashion.

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

prodingus

  • Jr. Member
  • **
  • Posts: 83
Re: cannot receive messages; udp server with lnet
« Reply #24 on: July 02, 2022, 08:43:41 pm »
Code: Pascal  [Select][+][-]
  1. program sockets_1;
  2.  
  3. uses
  4.         sysutils, crt, sockets;
  5.  
  6. const
  7.   UDPPackLen = 512;
  8. var
  9.   sock: Tsocket;
  10.   buff: Array[0..UDPPackLen-1] of Byte;
  11.  
  12.   outp:string;
  13.  
  14.   ServerAddr, FromAddr: sockaddr_in;
  15.   FromAddrLen: Integer;
  16.   ClientAddr: String;
  17.   ClientPort: Word;
  18.   MessageSize: SizeInt;
  19. begin
  20.   // Start UDP Server
  21.   sock := fpsocket(AF_INET, SOCK_DGRAM, 0);
  22.   ServerAddr.sin_family:= AF_INET;
  23.   ServerAddr.sin_addr := StrToNetAddr('1.1.1.2'); // server IP
  24.   ServerAddr.sin_port := 1234; // server port
  25.   fpbind(sock, @ServerAddr, SizeOf(ServerAddr));
  26.   // Receive Data
  27.   repeat begin
  28.   MessageSize := fprecvfrom(sock, @buff, SizeOf(buff), 0, @FromAddr, @FromAddrLen);
  29.   ClientAddr := NetAddrToStr(FromAddr.sin_addr);
  30.   ClientPort := NToHs(FromAddr.sin_port);
  31.  
  32.   SetString(outp, PAnsiChar(@buff[0]), 3);
  33.   writeln(outp);
  34.   sleep(100);
  35.   end; until false
  36. end.
  37.  

Unless I am wrong, this is not working. Hercules is receiveing, but this is not.
lazarus 2.2.0, FPC 3.2.2

rvk

  • Hero Member
  • *****
  • Posts: 6112
Re: cannot receive messages; udp server with lnet
« Reply #25 on: July 02, 2022, 09:38:36 pm »
You're binding your listener to 1.1.1.2? Is that the real addres??
Do you want broadcasts UDP or directed UDP? (also show some code of the server)

You have very little error checking in your code.

For example.
After
Code: Pascal  [Select][+][-]
  1. sock := fpsocket(AF_INET, SOCK_DGRAM, 0);
you could set an
Code: Pascal  [Select][+][-]
  1. assert(sock <> -1);
to check if the sock is really assigned.
(you won't get an error otherwise and nothing will work)

For the loop you should do:
Code: Pascal  [Select][+][-]
  1. if fpbind(.........) = 0 then
  2. begin
  3.   repeat
  4.   until
  5. end
  6. else
  7. begin
  8.   writeln(socketError);
  9. end;
The socketError will give you more information when your fpbind() fails.

Also... change
Code: Pascal  [Select][+][-]
  1. MessageSize := fprecvfrom(.......)
to
Code: Pascal  [Select][+][-]
  1. MessageSize := fprecvfrom(.......)
  2. if MessageSize = -1 then writeln(socketError);

That will also give you a lot more information.

You have port 1234. I do think you need to use htons(1234) but I'm not sure (and you would need to do that also on the server side).
(I'm not that familiar with fpsock)

You also haven't shown anything on the server side and specified anything about the network IP's.
« Last Edit: July 02, 2022, 09:40:45 pm by rvk »

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: cannot receive messages; udp server with lnet
« Reply #26 on: July 02, 2022, 09:43:14 pm »
Not working /how/? What's going wrong? the fpBind() or the receive? What's in SocketError?

The rather hackish way I'd do it would be something like

Code: Pascal  [Select][+][-]
  1.     uses
  2.       sockets;
  3.      
  4.     const
  5.       UDPPackLen = 512;
  6.  
  7.     var
  8.       sock: Tsocket;
  9.       buff: AnsiString;
  10.       ServerAddr, FromAddr: sockaddr_in;
  11.       FromAddrLen: Integer;
  12.       ClientAddr: String;
  13.       ClientPort: Word;
  14.       MessageSize: SizeInt;
  15.     begin
  16.       // Start UDP Server
  17.       sock := fpsocket(AF_INET, SOCK_DGRAM, 0);
  18.       ServerAddr.sin_family:= AF_INET;
  19.       ServerAddr.sin_addr := StrToNetAddr('1.1.1.2'); // server IP
  20.       ServerAddr.sin_port := 531; // server port
  21.       fpbind(sock, @ServerAddr, SizeOf(ServerAddr));
  22.       // Receive Data
  23.       SetLength(buff, UDPPackLen);
  24.       MessageSize := fprecvfrom(sock, @buff[1], Length(buff), 0, @FromAddr, @FromAddrLen);
  25.       SetLength(buff, MessageSize);
  26.       ClientAddr := NetAddrToStr(FromAddr.sin_addr);
  27.       ClientPort := NToHs(FromAddr.sin_port);
  28.       WriteLn(buff)
  29.     end.
  30.  

Note that that's untested, I'm in the middle of doing my year-end books /and/ my usual development system has blown up. But it has the advantage that you can see exactly what's in the string using e.g. the Lazarus debugger, noting of course that element
  • doesn't exist.


(Modified to emphasise that the buffer should be an AnsiString, i.e. one byte pre character)

MarkMLl
« Last Edit: July 03, 2022, 11:58:13 am by 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

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: cannot receive messages; udp server with lnet
« Reply #27 on: July 02, 2022, 10:20:15 pm »
I got really bored and wrote a small wrapper around the sockets API that makes it easier to use and less C-ish: https://github.com/Warfley/PasSimpleSockets/tree/master
Just this one Unit:
Code: Pascal  [Select][+][-]
  1. unit SimpleSockets;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   SysUtils, Sockets, {$IfDef WINDOWS}WinSock2{$Else}BaseUnix{$EndIf};
  9.  
  10. type
  11.  
  12.   { Basic Socket Types }
  13.  
  14.   TSocketFD = Sockets.Tsocket;
  15.   TSocketType = (stIPv4, stIPv6, stDualStack);
  16.   TSocket = record
  17.     FD: TSocketFD;
  18.     SocketType: TSocketType;
  19.   end;
  20.  
  21.   TAddressType = (atIN4, atIN6);
  22.   TNetworkAddress = record
  23.     Address: String;
  24.     AddressType: TAddressType;
  25.   end;
  26.  
  27.   { UDP Return Types }
  28.  
  29.   TUDPResult= record
  30.     FromAddr: TNetworkAddress;
  31.     FromPort: Word;
  32.     DataSize: SizeInt;
  33.   end;
  34.  
  35.   generic TUDPDataMessage<T> = record
  36.     FromAddr: TNetworkAddress;
  37.     FromPort: Word;
  38.     Data: T;
  39.   end;
  40.  
  41.   TUDPStringMessage = specialize TUDPDataMessage<String>;
  42.  
  43.   { Exceptions }
  44.  
  45.   EDualStackNotSupported = class(Exception);
  46.   EUnsupportedAddress = class(Exception);
  47.  
  48.   { ESocketError }
  49.  
  50.   ESocketError = class(Exception)
  51.   private
  52.     FCode: Integer;
  53.   public
  54.     constructor Create(ACode: Integer; const FunName: String);
  55.  
  56.     property Code: Integer read FCode;
  57.   end;
  58.  
  59.   EConnectionClosedException = class(Exception);
  60.   EUDPFragmentationException = class(Exception);
  61.  
  62. const
  63.   MaxUDPPackageSize = 512;
  64.  
  65.   { Address Management }
  66.  
  67. function IN4Address(const Address: String): TNetworkAddress; inline;
  68. function IN6Address(const Address: String): TNetworkAddress; inline;
  69. function IN4MappedIN6Address(const In4Address: String): TNetworkAddress; inline;
  70. function INAddr(const Address: String): TNetworkAddress; inline;
  71.  
  72. function IsIPv4Mapped(const IPv6Addr: TNetworkAddress): Boolean; inline;
  73. function ExtractIPv4Address(const IPv6Addr: TNetworkAddress): TNetworkAddress; inline;
  74.  
  75. function IN6Equal(const A, B: String): Boolean;
  76. operator =(const A, B: TNetworkAddress): Boolean; inline;
  77. operator :=(const AStr: String): TNetworkAddress; inline;
  78.  
  79.   { Socket Functions }
  80.  
  81. function TCPSocket(AType: TSocketType): TSocket; inline;
  82. function UDPSocket(AType: TSocketType): TSocket; inline;
  83.  
  84. procedure CloseSocket(const ASocket: TSocket); inline;
  85.  
  86. procedure Bind(const ASocket: TSocket; const AAddress: TNetworkAddress; APort: Word);
  87. procedure TCPServerListen(const ASocket: TSocket; Backlog: Integer); inline;
  88.  
  89. function TCPReceive(const ASocket: TSocket; ABuffer: Pointer; MaxSize: SizeInt; AFlags: Integer = 0): SizeInt; inline;
  90. function UDPReceive(const ASocket: TSocket; ABuffer: Pointer; MaxSize: SizeInt; AFlags: Integer = 0): TUDPResult;
  91. function TCPSend(const ASocket: TSocket; ABuffer: Pointer; ASize: SizeInt; AFlags: Integer = 0): SizeInt; inline;
  92. function UDPSend(const ASocket: TSocket; const ReceiverAddr: TNetworkAddress;
  93.                   ReceiverPort: Word; ABuffer: Pointer; ASize: SizeInt; AFlags: Integer = 0): SizeInt; inline;
  94.  
  95. function TCPReceiveStr(const ASocket: TSocket; MaxLength: SizeInt; AFlags: Integer = 0): String; inline;
  96. function UDPReceiveStr(const ASocket: TSocket; MaxLength: SizeInt = MaxUDPPackageSize; AFlags: Integer = 0): TUDPStringMessage; inline;
  97. function TCPSendStr(const ASocket: TSocket; const AData: String; AFlags: Integer = 0): SizeInt; inline;
  98. function UDPSendStr(const ASocket: TSocket; const ReceiverAddr: TNetworkAddress; ReceiverPort: Word; const AData: String; AFlags: Integer = 0): SizeInt; inline;
  99.  
  100. generic function TCPReceive<T>(const ASocket: TSocket; AFlags: Integer = 0): T; inline;
  101. generic function UDPReceive<T>(const ASocket: TSocket; AFlags: Integer = 0): specialize TUDPDataMessage<T>; inline;
  102. generic function TCPSend<T>(const ASocket: TSocket; constref AData: T; AFlags: Integer = 0): SizeInt; inline;
  103. generic function UDPSend<T>(const ASocket: TSocket; constref ReceiverAddr: TNetworkAddress; ReceiverPort: Word; const AData: T; AFlags: Integer = 0): SizeInt; inline;
  104.  
  105. generic function TCPReceiveArray<T>(const ASocket: TSocket; MaxCount: SizeInt; AFlags: Integer = 0): specialize TArray<T>; inline;
  106. generic function UDPReceiveArray<T>(const ASocket: TSocket; MaxCount: SizeInt; AFlags: Integer = 0): specialize TUDPDataMessage<specialize TArray<T>>; inline;
  107. generic function TCPSendArray<T>(const ASocket: TSocket; const AData: specialize TArray<T>; AFlags: Integer = 0): SizeInt; inline;
  108. generic function UDPSendArray<T>(const ASocket: TSocket; const ReceiverAddr: TNetworkAddress; ReceiverPort: Word; const AData: specialize TArray<T>; AFlags: Integer = 0): SizeInt; inline;
  109.  
  110. // Timeout in MS
  111. function DataPending(const ASocket: TSocket; TimeOut: Integer = 0): Boolean;
  112.  
  113. { Helper }
  114. // Must be in interface because of generic functions
  115.  
  116. type
  117.   _PAddressUnion = ^_TAddressUnion;
  118.   _TAddressUnion = record
  119.   case Boolean of
  120.   True: (In4Addr: Sockets.sockaddr_in);
  121.   False: (In6Addr: Sockets.sockaddr_in6);
  122.   end;
  123.  
  124. procedure FillAddr(AAddress: TNetworkAddress; APort: Word; Addr: _PAddressUnion; DualStack: Boolean); inline;
  125. procedure ReadAddr(Addr: _PAddressUnion; DualStack: Boolean; out AAddress: TNetworkAddress; out APort: Word);
  126. implementation
  127.  
  128. const
  129.   IPPROTO_IPV6 = {$IfDef WINDOWS}41{$Else}41{$EndIf};
  130.   IPV6_V6ONLY = {$IfDef WINDOWS}27{$Else}26{$EndIf};
  131.  
  132. procedure FillAddr(AAddress: TNetworkAddress; APort: Word;
  133.   Addr: _PAddressUnion; DualStack: Boolean);
  134. begin
  135.   if (AAddress.AddressType = atIN4) and DualStack then
  136.     AAddress := IN4MappedIN6Address(AAddress.Address);
  137.   if AAddress.AddressType = atIN4 then
  138.   begin
  139.     Addr^.In4Addr.sin_family := AF_INET;
  140.     Addr^.In4Addr.sin_port := HToNS(APort);
  141.     Addr^.In4Addr.sin_addr.s_addr := LongWord(StrToNetAddr(AAddress.Address));
  142.   end
  143.   else if AAddress.AddressType = atIN6 then
  144.   begin
  145.     Addr^.In6Addr.sin6_family := AF_INET6;
  146.     Addr^.In6Addr.sin6_port := HToNS(APort);
  147.     Addr^.In6Addr.sin6_addr := StrToHostAddr6(AAddress.Address);
  148.     Addr^.In6Addr.sin6_flowinfo := 0;
  149.     Addr^.In6Addr.sin6_scope_id := 0;
  150.   end
  151.   else
  152.     raise EUnsupportedAddress.Create('Address type ' + ord(AAddress.AddressType).ToString + ' not supported');
  153. end;
  154.  
  155. procedure ReadAddr(Addr: _PAddressUnion; DualStack: Boolean; out
  156.   AAddress: TNetworkAddress; out APort: Word);
  157. begin
  158.   if Addr^.In4Addr.sin_family = AF_INET then
  159.   begin
  160.     AAddress := IN4Address(NetAddrToStr(Addr^.In4Addr.sin_addr));
  161.     APort := NToHs(Addr^.In4Addr.sin_port);
  162.   end
  163.   else if Addr^.In6Addr.sin6_family = AF_INET6 then
  164.   begin
  165.     AAddress := IN6Address(HostAddrToStr6(Addr^.In6Addr.sin6_addr));
  166.     if DualStack and IsIPv4Mapped(AAddress.Address) then
  167.       AAddress := ExtractIPv4Address(AAddress);
  168.     APort := NToHs(Addr^.In6Addr.sin6_port);
  169.   end
  170.   else
  171.     raise EUnsupportedAddress.Create('Address Family ' + Addr^.In4Addr.sin_family.ToString + ' not supported');
  172. end;
  173.  
  174. function SocketInvalid(ASocket: TSocketFD): Boolean; inline;
  175. begin
  176.   {$IfDef Windows}
  177.   Result := ASocket = TSocketFD(INVALID_SOCKET);
  178.   {$Else}
  179.   Result := ASocket < 0;
  180.   {$EndIf}
  181. end;
  182.  
  183. function CreateSocketFD(ADomain: TSocketType; AType: Integer; AProto: Integer): TSocketFD;
  184. var
  185.   AFam, v6Only: Integer;
  186. begin
  187.   if ADomain = stIPv4 then
  188.     AFam := AF_INET
  189.   else
  190.     AFam := AF_INET6;
  191.   Result := fpsocket(AFam, AType, AProto);
  192.   if SocketInvalid(Result) then
  193.     raise ESocketError.Create(socketerror, 'socket');
  194.  
  195.   if ADomain = stDualStack then
  196.   begin
  197.     v6Only := 0;
  198.     if fpsetsockopt(Result, IPPROTO_IPV6, IPV6_V6ONLY, @v6Only, SizeOf(v6Only)) <> 0 then
  199.       raise EDualStackNotSupported.Create('Dualstack option not supported on this system: ' + socketerror.ToString);
  200.   end;
  201. end;
  202.  
  203. function IN4Address(const Address: String): TNetworkAddress;
  204. begin
  205.  Result := Default(TNetworkAddress);
  206.  Result.Address := Address;
  207.  Result.AddressType := atIN4;
  208. end;
  209.  
  210. function IN6Address(const Address: String): TNetworkAddress;
  211. begin
  212.  Result := Default(TNetworkAddress);
  213.  Result.Address := Address;
  214.  Result.AddressType := atIN6;
  215. end;
  216.  
  217. function IN4MappedIN6Address(const In4Address: String): TNetworkAddress;
  218. var
  219.   InAddr: TIn_addr;
  220. begin
  221.   InAddr := StrToNetAddr(In4Address);
  222.   Result := IN6Address('::FFFF:%x:%x'.Format([(InAddr.s_bytes[1] shl 8) or InAddr.s_bytes[2],
  223.                                               (InAddr.s_bytes[3] shl 8) or InAddr.s_bytes[4]]));
  224. end;
  225.  
  226. function INAddr(const Address: String): TNetworkAddress;
  227. begin
  228.  Result := Default(TNetworkAddress);
  229.   if Pos(':', Address) = 0 then
  230.     Result.AddressType := atIN4
  231.   else
  232.     Result.AddressType := atIN6;
  233.   Result.Address := Address;
  234. end;
  235.  
  236. function IsIPv4Mapped(const IPv6Addr: TNetworkAddress): Boolean;
  237. var
  238.   In6Addr: Sockets.TIn6Addr;
  239. begin
  240.   if IPv6Addr.AddressType = atIN4 then
  241.     Exit(True);
  242.   if IPv6Addr.AddressType  <> atIN6 then
  243.     raise EUnsupportedAddress.Create('Can only check IPv4 mapping for IPv6 addresses');
  244.   IN6Addr := StrToHostAddr6(IPv6Addr.Address);
  245.   Result := (IN6Addr.u6_addr16[0] = 0) and
  246.             (IN6Addr.u6_addr16[1] = 0) and
  247.             (IN6Addr.u6_addr16[2] = 0) and
  248.             (IN6Addr.u6_addr16[3] = 0) and
  249.             (IN6Addr.u6_addr16[4] = 0) and
  250.             (IN6Addr.u6_addr16[5] = $FFFF);
  251. end;
  252.  
  253. function ExtractIPv4Address(const IPv6Addr: TNetworkAddress): TNetworkAddress;
  254. var
  255.   In6Addr: Sockets.TIn6Addr;
  256. begin
  257.   if IPv6Addr.AddressType = atIN4 then
  258.     Exit(IPv6Addr);
  259.   if IPv6Addr.AddressType  <> atIN6 then
  260.     raise EUnsupportedAddress.Create('Can only extract IPv4 mapping from IPv6 addresses');
  261.   IN6Addr := StrToHostAddr6(IPv6Addr.Address);
  262.   Result := IN4Address('%d.%d.%d.%d'.Format([IN6Addr.s6_addr8[12],
  263.                                              IN6Addr.s6_addr8[13],
  264.                                              IN6Addr.s6_addr8[14],
  265.                                              IN6Addr.s6_addr8[15]]));
  266. end;
  267.  
  268. function IN6Equal(const A, B: String): Boolean;
  269. var
  270.   AAddr, BAddr: Sockets.Tin6_addr;
  271. begin
  272.   AAddr := StrToHostAddr6(A);
  273.   BAddr := StrToHostAddr6(B);
  274.   Result := (AAddr.s6_addr32[0] = BAddr.s6_addr32[0]) and
  275.             (AAddr.s6_addr32[1] = BAddr.s6_addr32[1]) and
  276.             (AAddr.s6_addr32[2] = BAddr.s6_addr32[2]) and
  277.             (AAddr.s6_addr32[3] = BAddr.s6_addr32[3]);
  278. end;
  279.  
  280. operator=(const A, B: TNetworkAddress): Boolean;
  281. begin
  282.   Result := (A.AddressType = B.AddressType) and (
  283.               ((A.AddressType = atIN4) and (A.Address = B.Address)) or // IPv4: simple string equality
  284.               ((A.AddressType = atIN6) and IN6Equal(A.Address, B.Address)) // IPv6 check binary equality
  285.             );
  286. end;
  287.  
  288. operator:=(const AStr: String): TNetworkAddress;
  289. begin
  290.   Result := INAddr(AStr);
  291. end;
  292.  
  293. function TCPSocket(AType: TSocketType): TSocket;
  294. begin
  295.   Result.SocketType := AType;
  296.   Result.FD := CreateSocketFD(AType, SOCK_STREAM, 0);
  297. end;
  298.  
  299. function UDPSocket(AType: TSocketType): TSocket;
  300. begin
  301.   Result.SocketType := AType;
  302.   Result.FD := CreateSocketFD(AType, SOCK_DGRAM, 0);
  303. end;
  304.  
  305. procedure CloseSocket(const ASocket: TSocket);
  306. begin
  307.   Sockets.CloseSocket(ASocket.FD);
  308. end;
  309.  
  310. procedure Bind(const ASocket: TSocket; const AAddress: TNetworkAddress;
  311.   APort: Word);
  312. var
  313.   addr: _TAddressUnion;
  314. begin
  315.   FillAddr(AAddress, APort, @addr, ASocket.SocketType = stDualStack);
  316.   if fpbind(ASocket.FD, @addr, SizeOf(addr)) <> 0 then raise
  317.     ESocketError.Create(socketerror, 'bind (%s:%d)'.Format([AAddress.Address, APort]));
  318. end;
  319.  
  320. procedure TCPServerListen(const ASocket: TSocket; Backlog: Integer);
  321. begin
  322.   if fplisten(ASocket.FD, Backlog) <> 0 then raise
  323.     ESocketError.Create(socketerror, 'listen');
  324. end;
  325.  
  326. function TCPReceive(const ASocket: TSocket; ABuffer: Pointer; MaxSize: SizeInt;
  327.   AFlags: Integer): SizeInt;
  328. begin
  329.   Result := fprecv(ASocket.FD, ABuffer, MaxSize, AFlags);
  330.   if Result = 0 then
  331.     raise EConnectionClosedException.Create('The connection closed')
  332.   else if Result < 0 then
  333.     raise ESocketError.Create(socketerror, 'recv');
  334. end;
  335.  
  336. function UDPReceive(const ASocket: TSocket; ABuffer: Pointer; MaxSize: SizeInt;
  337.   AFlags: Integer): TUDPResult;
  338. var
  339.   {$IfNDef WINDOWS}
  340.   _addr: _TAddressUnion;
  341.   _addrLen: SizeInt;
  342.   {$EndIf}
  343.   addr: _PAddressUnion;
  344.   addrLen: PSizeInt;
  345. begin
  346.   {$IfDef WINDOWS}
  347.   // WinSock doesn't like the addr located on the stack, therefore we create a heap instance for it
  348.   New(addr);
  349.   New(addrLen);
  350.   try
  351.   {$Else}
  352.   addr := @_addr;
  353.   addrLen := @_addrLen;
  354.   {$EndIf}
  355.   addrLen^ := SizeOf(_TAddressUnion);
  356.   Result.DataSize := fprecvfrom(ASocket.FD, ABuffer, MaxSize, AFlags, Sockets.PSockAddr(addr), addrLen);
  357.   if Result.DataSize < 0 then
  358.     raise ESocketError.Create(socketerror, 'recvfrom');
  359.   ReadAddr(addr, ASocket.SocketType = stDualStack, Result.FromAddr, Result.FromPort);
  360.   {$IfDef WINDOWS}
  361.   finally
  362.     Dispose(addr);
  363.     Dispose(addrLen);
  364.   end;
  365.   {$EndIf}
  366. end;
  367.  
  368. function TCPSend(const ASocket: TSocket; ABuffer: Pointer; ASize: SizeInt;
  369.   AFlags: Integer): SizeInt;
  370. begin
  371.   Result := fpsend(ASocket.FD, ABuffer, ASize, AFlags);
  372.   if Result < 0 then
  373.     raise ESocketError.Create(socketerror, 'send');
  374. end;
  375.  
  376. function UDPSend(const ASocket: TSocket; const ReceiverAddr: TNetworkAddress;
  377.   ReceiverPort: Word; ABuffer: Pointer; ASize: SizeInt; AFlags: Integer
  378.   ): SizeInt;
  379. var
  380.   addr: _TAddressUnion;
  381. begin
  382.   FillAddr(ReceiverAddr, ReceiverPort, @addr, ASocket.SocketType = stDualStack);
  383.   Result := fpsendto(ASocket.FD, ABuffer, ASize, AFlags, @addr, SizeOf(addr));
  384.   if Result < 0 then
  385.     raise ESocketError.Create(socketerror, 'sendto');
  386. end;
  387.  
  388. function TCPReceiveStr(const ASocket: TSocket; MaxLength: SizeInt;
  389.   AFlags: Integer): String;
  390. var
  391.   Len: SizeInt;
  392. begin
  393.   Result := '';
  394.   SetLength(Result, MaxLength);
  395.   Len := TCPReceive(ASocket, @Result[1], MaxLength, AFlags);
  396.   SetLength(Result, Len);
  397. end;
  398.  
  399. function UDPReceiveStr(const ASocket: TSocket; MaxLength: SizeInt;
  400.   AFlags: Integer): TUDPStringMessage;
  401. var
  402.   UdpMessage: TUDPResult;
  403. begin
  404.   Result := Default(specialize TUDPDataMessage<String>);
  405.   SetLength(Result.Data, MaxLength);
  406.   UdpMessage := UDPReceive(ASocket, @Result.Data[1], MaxLength, AFlags);
  407.   SetLength(Result.Data, UdpMessage.DataSize);
  408.   Result.FromAddr := UdpMessage.FromAddr;
  409.   Result.FromPort := UdpMessage.FromPort;
  410. end;
  411.  
  412. function TCPSendStr(const ASocket: TSocket; const AData: String; AFlags: Integer
  413.   ): SizeInt;
  414. begin
  415.   if Length(AData) = 0 then Exit(0);
  416.   Result := TCPSend(ASocket, @AData[1], Length(AData), AFlags);
  417. end;
  418.  
  419. function UDPSendStr(const ASocket: TSocket;
  420.   const ReceiverAddr: TNetworkAddress; ReceiverPort: Word; const AData: String; AFlags: Integer
  421.   ): SizeInt;
  422. begin
  423.   if Length(AData) = 0 then Exit(0);
  424.   Result := UDPSend(ASocket, ReceiverAddr, ReceiverPort, @AData[1], Length(AData), AFlags);
  425. end;
  426.  
  427. generic function TCPReceive<T>(const ASocket: TSocket; AFlags: Integer = 0): T;
  428. var
  429.   Len: SizeInt;
  430. begin
  431.   Result := Default(T);
  432.   Len := 0;
  433.   while Len < SizeOf(Result) do
  434.     Len += TCPReceive(ASocket, @PByte(@Result)[Len], SizeOf(Result) - Len, AFlags);
  435. end;
  436.  
  437. generic function UDPReceive<T>(const ASocket: TSocket; AFlags: Integer = 0): specialize TUDPDataMessage<T>;
  438. var
  439.   UdpMessage: TUDPResult;
  440. begin
  441.   Result := Default(T);
  442.   UdpMessage := UDPReceive(ASocket, @Result, SizeOf(Result), AFlags);
  443.   if UdpMessage.DataSize < SizeOf(T) then
  444.     raise EUDPFragmentationException.Create('Receiving of fragmented data is not supported by typed receive');
  445.   Result.FromAddr := UdpMessage.FromAddr;
  446.   Result.FromPort := UdpMessage.FromPort;
  447. end;
  448.  
  449. generic function TCPSend<T>(const ASocket: TSocket; constref AData: T; AFlags: Integer = 0): SizeInt;
  450. begin
  451.   Result := TCPSend(ASocket, @AData, SizeOf(T), AFlags);
  452. end;
  453.  
  454. generic function UDPSend<T>(const ASocket: TSocket; const ReceiverAddr: TNetworkAddress; ReceiverPort: Word; constref AData: T; AFlags: Integer = 0): SizeInt;
  455. begin
  456.   Result := UDPSend(ASocket, ReceiverAddr, ReceiverPort, @AData, SizeOf(T), AFlags);
  457. end;
  458.  
  459. generic function TCPReceiveArray<T>(const ASocket: TSocket; MaxCount: SizeInt; AFlags: Integer = 0): specialize TArray<T>;
  460. var
  461.   Len: SizeInt;
  462. begin    
  463.   Result := nil;
  464.   SetLength(Result, MaxCount);
  465.   Len := 0;
  466.   repeat
  467.     Len += TCPReceive(ASocket, @PByte(@Result)[Len], MaxCount * SizeOf(T) - Len, AFlags);
  468.   until (Len mod SizeOf(T)) = 0;
  469.   SetLength(Result, Len div SizeOf(T));
  470. end;
  471.  
  472. generic function UDPReceiveArray<T>(const ASocket: TSocket; MaxCount: SizeInt; AFlags: Integer = 0): specialize TUDPDataMessage<specialize TArray<T>>;
  473. var
  474.   UdpMessage: TUDPResult;
  475. begin
  476.   Result.Data := nil;
  477.   SetLength(Result.Data, MaxCount);
  478.   UdpMessage := UDPReceive(ASocket, @Result.Data[0], MaxCount * SizeOf(T), AFlags);
  479.   if UdpMessage.DataSize mod SizeOf(T) > 0 then
  480.     raise EUDPFragmentationException.Create('Receiving of fragmented data is not supported by typed receive');
  481.   SetLength(Result.Data, UdpMessage.DataSize div SizeOf(T));
  482.   Result.FromAddr := UdpMessage.FromAddr;
  483.   Result.FromPort := UdpMessage.FromPort;
  484. end;
  485.  
  486. generic function TCPSendArray<T>(const ASocket: TSocket; const AData: specialize TArray<T>; AFlags: Integer = 0): SizeInt;
  487. begin
  488.   if Length(AData) = 0 then Exit(0);
  489.   Result := TCPSend(ASocket, @AData[0], Length(AData) * SizeOf(T), AFlags);
  490. end;
  491.  
  492. generic function UDPSendArray<T>(const ASocket: TSocket; const ReceiverAddr: TNetworkAddress; ReceiverPort: Word; const AData: specialize TArray<T>; AFlags: Integer = 0): SizeInt;
  493. begin
  494.   if Length(AData) = 0 then Exit(0);
  495.   Result := UDPSend(ASocket, ReceiverAddr, ReceiverPort, @AData[0], Length(AData) * SizeOf(T), AFlags);
  496. end;
  497.  
  498. function DataPending(const ASocket: TSocket; TimeOut: Integer): Boolean;
  499. var
  500.   FDSet: TFDSet;
  501.   timeval: TTimeVal;
  502.   Ret: LongInt;
  503. begin
  504.   {$IfDef UNIX}fp{$endif}FD_ZERO(FDSet);
  505.   {$IfDef UNIX}fp{$endif}FD_SET(ASocket.FD, FDSet);
  506.   timeval.tv_sec := TimeOut div 1000;
  507.   timeval.tv_usec := (TimeOut mod 1000) * 1000;
  508.   Ret := {$IfDef UNIX}fp{$endif}select(ASocket.FD, @FDSet, nil, nil, @timeval);
  509.   if Ret < 0 then
  510.     raise ESocketError.Create(socketerror, 'select');
  511.   Result := Ret > 0;
  512. end;
  513.  
  514. { ESocketError }
  515.  
  516. constructor ESocketError.Create(ACode: Integer; const FunName: String);
  517. begin
  518.   inherited CreateFmt('[Socket Error: %d] %s call failed',  [ACode, FunName]);
  519.   FCode := ACode;
  520. end;
  521.  
  522. end.

Example:
Code: Pascal  [Select][+][-]
  1. program example;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   SimpleSockets;
  7.  
  8. var
  9.   Sock: TSocket;
  10.   Msg: TUDPStringMessage;
  11. begin
  12.   Sock := UDPSocket(stDualStack);
  13.   try
  14.     Bind(Sock, '127.0.0.1', 1337);
  15.     // Simple echo server: Receive message and answer with same message
  16.     Msg := UDPReceiveStr(Sock);
  17.     WriteLn('Received from Client at ', Msg.FromAddr.Address, ':', Msg.FromPort,' message: ', Msg.Data);
  18.     UDPSendStr(Sock, Msg.FromAddr, Msg.FromPort, Msg.Data);
  19.   finally
  20.     CloseSocket(Sock);
  21.   end;
  22.   ReadLn;
  23. end.
To use just copy the unit into your project and get started

PS: this is something I wanted to do for quite some time, it is basically the AsyncNet version just not asynchronous. Because I think using sockets for small things (like a small UDP client or server) is way to complicated right now. But it defenitely needs completion, as it just contains the basic socket functions
Also this was a test about DualStack implementations I wanted to improve in asyncnet, and this seems to work quite well and transparently

@prodingus
Feel free to use it, did some simple tests and at least for this it works quite well
« Last Edit: July 02, 2022, 10:38:36 pm by Warfley »

rvk

  • Hero Member
  • *****
  • Posts: 6112
Re: cannot receive messages; udp server with lnet
« Reply #28 on: July 02, 2022, 10:27:21 pm »
I also made a very very very crude sample.

Sender and Receiver.
At least this will print out the errors TS might get.

Code: Pascal  [Select][+][-]
  1. program Sender;
  2. uses
  3.   SysUtils, crt, sockets;
  4. const
  5.   UDPPackLen = 512;
  6. var
  7.   sock: Tsocket;
  8.   buff: array[0..UDPPackLen - 1] of char;
  9.   ServerAddr: sockaddr_in;
  10.   MessageSize: SizeInt;
  11. begin
  12.   sock := fpsocket(AF_INET, SOCK_DGRAM, 0);
  13.   assert(sock <> -1);
  14.   ServerAddr.sin_family := AF_INET;
  15.   ServerAddr.sin_addr := StrToNetAddr('192.168.2.11'); // server IP
  16.   ServerAddr.sin_port := htons(1234); // server port
  17.   // if fpbind(sock, @ServerAddr, SizeOf(ServerAddr)) = 0 then
  18.   if fpconnect(sock, @ServerAddr, SizeOf(ServerAddr)) = 0 then
  19.   begin
  20.     repeat
  21.       buff := timetostr(now) + #0;
  22.       MessageSize := fpsend(Sock, @Buff[0], 9, 0);
  23.       if MessageSize = -1 then
  24.       begin
  25.         writeln(socketError);
  26.       end
  27.       else
  28.       begin
  29.         writeln('Sending: ' + timetostr(now) + ' ' + MessageSize.ToString);
  30.       end;
  31.       sleep(100);
  32.     until False;
  33.   end
  34.   else
  35.   begin
  36.     writeln(socketError);
  37.     readln;
  38.   end;
  39. end.

Code: Pascal  [Select][+][-]
  1. program Receiver;
  2. uses
  3.   SysUtils, crt, sockets;
  4. const
  5.   UDPPackLen = 512;
  6. var
  7.   sock: Tsocket;
  8.   buff: array[0..UDPPackLen - 1] of char;
  9.   ServerAddr: sockaddr_in;
  10.   MessageSize: SizeInt;
  11.   outp: string;
  12. begin
  13.   sock := fpsocket(AF_INET, SOCK_DGRAM, 0);
  14.   assert(sock <> -1);
  15.   ServerAddr.sin_family := AF_INET;
  16.   ServerAddr.sin_addr := StrToNetAddr('192.168.2.11'); // server IP
  17.   ServerAddr.sin_port := htons(1234); // server port
  18.   if fpbind(sock, @ServerAddr, SizeOf(ServerAddr)) = 0 then
  19.   begin
  20.     repeat
  21.       MessageSize := fprecv(sock, @buff, SizeOf(buff), 0);
  22.       if MessageSize = -1 then
  23.       begin
  24.         writeln(socketError);
  25.       end
  26.       else
  27.       begin
  28.         SetString(outp, pansichar(@buff[0]), 8);
  29.         writeln('Received: ' + outp);
  30.       end;
  31.       sleep(100);
  32.     until False;
  33.   end
  34.   else
  35.   begin
  36.     writeln(socketError);
  37.     readln;
  38.   end;
  39. end.

Works fine here (after allowing communication through the firewall).

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: cannot receive messages; udp server with lnet
« Reply #29 on: July 02, 2022, 10:57:01 pm »
Because I think using sockets for small things (like a small UDP client or server) is way to complicated right now. But it defenitely needs completion, as it just contains the basic socket functions

UDP (and the equivalents via unix-domain sockets and whatever Windows offers these days) is vastly underused.

But what would be really useful is a subset SCTP (message-oriented but reliable) which was also small enough to put on e.g. an Arduino endpoint.

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

 

TinyPortal © 2005-2018