Recent

Author Topic: Simple sockets example for sending and receiving UDP packets  (Read 1213 times)

prodingus

  • Jr. Member
  • **
  • Posts: 83
Hi!

As the title says, I need a very basic UDP send/receive example:

a client loop that  sends continiously a message (to an ip,port),
and a server loop that listens (receives) for any messages

Ps, I am noob at networking.
Side note: I am using 2 windows 7 vms,  ips are 1.1.1.1 and 1.1.1.2, no firewall.

 
« Last Edit: July 02, 2022, 06:21:59 pm by prodingus »
lazarus 2.2.0, FPC 3.2.2

Leledumbo

  • Hero Member
  • *****
  • Posts: 8746
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Simple sockets example for sending and receiving UDP packets
« Reply #1 on: July 13, 2022, 08:18:57 pm »
Unfortunately, apart from BSD sockets unit, there's no pre-shipped UDP packages. fcl-net only contains TCP. However, lNet is just a few clicks away from the online package manager. After installation, check its examples folder for UDP both CLI and GUI. The CLI one is a single program that can be both client and server, just pass -s to be a server instead of a client.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Simple sockets example for sending and receiving UDP packets
« Reply #2 on: July 16, 2022, 10:51:37 pm »
This is the simplest I've got to hand. It's not entirely standalone since it's part of my business software, but it relies on a periodic poll rather than a background thread etc.

Code: Pascal  [Select][+][-]
  1. (* Return a socket suitable for DNS queries, or an error code as a negative
  2.   number. This is unprivileged, but is opened here so that the router (ping)
  3.   and DNS client code can be kept as similar as possible.
  4. *)
  5. function DnsClientSocket(): TSocket;
  6.  
  7. begin
  8.   result := dnsClientSocketHandle
  9. end { DnsClientSocket } ;
  10.  
  11.  
  12. (* Send a query to a randomly-selected DNS server, return the saved sequence
  13.   number or 0xffff on error. If not in error, the MSB of the sequence number is
  14.   set iff the address is local (RFC1918 etc.).
  15. *)
  16. function SendDnsQuery(): TDnsSequence;
  17.  
  18. (* See e.g. https://www2.cs.duke.edu/courses/fall16/compsci356/DNS/DNS-primer.pdf *)
  19.  
  20. const
  21.   standardQuery= $0000;
  22.   inverseQuery=  $0800;
  23.   statusQuery=   $1000;
  24.  
  25.   query= #1 + '1' +
  26.          #1 + '0' +
  27.          #1 + '0' +
  28.          #3 + '127' +
  29.          #7 + 'in-addr' +
  30.          #4 + 'arpa' +
  31.          #0 +
  32.          #$00 + #$0c +                  (* Query type, network-endian           *)
  33.          #$00 + #$01;                   (* Query class, network-endian          *)
  34.  
  35. var
  36.   server: string;
  37.   a: in4_addr;
  38.   sockAddr: TSockAddr;
  39.   sz: integer;
  40.   msg: TDnsMsg;
  41.  
  42.  
  43.   function localAddress(a: longword): boolean;
  44.  
  45.   begin
  46.     result := false;
  47.     if a and $ff000000 = (10 << 24) then
  48.       exit(true);
  49.     if a and $ffff0000 = (192 << 24) + (168 << 16) then
  50.       exit(true);
  51.     if a and $ff000000 = (172 << 24) then
  52.       if a and $00f00000 <> $00000000 then
  53.         exit(true);
  54.     if a and $ffff0000 = (169 << 24) + (254 << 16) then
  55.       exit(true)
  56.   end { localAddress } ;
  57.  
  58.  
  59. begin
  60.   if DnsClientSocket <= 0 then
  61.     exit($ffff);
  62.   if not Assigned(nameservers) or (nameservers.Count = 0) then
  63.     needsReload := true;
  64.   if needsReload then
  65.     if not loadResolvConf() then begin
  66. {$ifdef DEBUG }
  67.       WriteLn('DNS query abort -----');
  68. {$endif DEBUG }
  69.       exit($ffff)
  70.     end else
  71.       needsReload := false;
  72.   server := nameservers[0];
  73.   nameservers.Delete(0);
  74.  
  75. (* If we've just deleted the last server from the nameservers list, reread the  *)
  76. (* /etc/resolv.conf file. If this returns more than one item then repeat this   *)
  77. (* until the first entry in the nameservers list is not the same as the one     *)
  78. (* that we're about to use, so that if it fails we're not trying the same       *)
  79. (* server twice in succession. This is a noteworthy but justifiable departure   *)
  80. (* from randomness.                                                             *)
  81.  
  82.   if nameservers.Count = 0 then
  83.     repeat
  84.       if not loadResolvConf() then
  85.         break;                          (* List is left empty                   *)
  86.       if nameservers.Count = 1 then
  87.         break;                          (* No alternative to a repeat address   *)
  88.     until nameservers[0] <> server;
  89. {$ifdef DEBUG }
  90.   WriteLn('DNS query ', server, ' -----');
  91. {$endif DEBUG }
  92.   a := in4_addr(StrToHostAddr(server));
  93.   if a = in4_null then
  94.     exit($ffff);
  95.   FillChar(sockAddr, SizeOf(sockAddr), 0);
  96.   sockAddr.sa_family:= AF_INET;
  97.   sockAddr.sin_port:= hToNS(53);
  98.   sockAddr.sin_addr.s_addr := htoNL(longword(a));
  99.   result := Random($7fff);
  100.   if localAddress(longword(a)) then
  101.     result += $8000;                    (* Maximum possible is $7ffe + $8000    *)
  102.   FillChar(msg, SizeOf(msg), 0);
  103.   with msg do begin
  104.     hdr.id := htons(result);
  105.     hdr.flags := htons(standardQuery);
  106.     hdr.cnts[2] := htons(1)
  107.   end;
  108.   Move(query[1], msg.body[0], Length(query));
  109.   sz := SizeOf(TDnsHdr) + Length(query);
  110. //  while sz mod 4 <> 0 do
  111. //    sz += 1;
  112.   if fpSendTo(DnsClientSocket, @msg, sz, 0, @sockAddr, SizeOf(sockAddr)) < 0 then
  113.     result := $ffff;
  114. {$ifdef DEBUG }
  115.   WriteLn('DNS seq: ', result)
  116. {$endif DEBUG }
  117. end { SendDnsQuery } ;
  118.  
  119.  
  120. (* Retrieve the result of a query returned by a DNS server. Return false if
  121.   there is no result or the sequence number doesn't match.
  122. *)
  123. function PollDnsResult(const seq: TDnsSequence): TDnsResponse;
  124.  
  125. var
  126.   sockAddr: TSockAddr;
  127.   saLength, sz: integer;
  128.   msg: TDnsMsg;
  129.  
  130. begin
  131.   result := DnsNoResponse;
  132.   if (DnsClientSocket <= 0) or (seq = $ffff) then
  133.     exit;
  134.   FillChar(sockAddr, SizeOf(sockAddr), 0);
  135.   saLength := SizeOf(sockAddr);
  136.   FillChar(msg, SizeOf(msg), $ff);
  137.   sz := fpRecvFrom(DnsClientSocket, @msg, SizeOf(msg), MSG_DONTWAIT, @sockAddr, @saLength);
  138.   if sz <= SizeOf(TDnsHdr) then
  139.     exit;
  140. {$ifdef DEBUG }
  141.   Write('DNS rsp: ', seq);
  142.   WriteLn(' got: ', ntohs(msg.hdr.id));
  143. {$endif DEBUG }
  144.  
  145. (* Any coherent response is adequate confirmation that we can get through to    *)
  146. (* at least one server.                                                         *)
  147.  
  148.   if msg.hdr.id = htons(seq) then
  149.     result := DnsOkResponse
  150.   else
  151.     result := DnsBadResponse;
  152. {$ifdef DEBUG }
  153.   if result = DnsOkResponse then
  154.     WriteLn('DNS OK')
  155.   else
  156.     WriteLn('DNS Fail')
  157. {$endif DEBUG }
  158. end { PollDnsResult } ;
  159.  
  160.  
  161. // Elsewhere
  162.  
  163. var
  164.   dnsClientSocketHandle: TSocket= -32768;
  165.  
  166.   Write(StdErr, 'Creating new Internet domain connectionless client socket... ');
  167.   dnsClientSocketHandle := fpSocket(PF_INET, SOCK_DGRAM, 0);
  168.   if dnsClientSocketHandle > 0 then
  169.     WriteLn(StdErr, 'OK')
  170.   else begin
  171.     WriteLn(StdErr, 'failed, might need CAP_DAC_OVERRIDE,CAP_NET_BIND_SERVICE,CAP_NET_RAW=p+e');
  172.     dnsClientSocketHandle := -Abs(ErrNo)
  173.   end;
  174.  
  175.  
  176.     dnsQuery := SendDnsQuery();         (* Response should turn it green        *)
  177.     dnsQueryTime := Now();
  178.  
  179.  
  180.         case PollDnsResult(dnsQuery) of
  181.           DNSWrongResponse: ;
  182.           DnsOkResponse:    begin
  183.                               if (dnsQuery and $8000) = $8000 then
  184.                                 DispatchWatchdog(IniFilesLocation.GetHostName() + '/' + SpecialName + '.DNS:0,13')
  185.                               else
  186.                                 DispatchWatchdog(IniFilesLocation.GetHostName() + '/' + SpecialName + '.DNS:0,22');
  187.                               dnsQuery := $ffff
  188.                             end
  189.         otherwise                       (* Timeout on no or bad response        *)
  190.           if Now() - dnsQueryTime > 46 * OneSecond then
  191.             dnsQuery := $ffff
  192.         end
  193.  

Most of the complexity there is obviously from setting up the message, and doing something with the result.

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