Network function SendTo for UDP Server (All OS)

Share your advanced PureBasic knowledge/code with the community.
User avatar
mk-soft
Always Here
Always Here
Posts: 6248
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Network function SendTo for UDP Server (All OS)

Post by mk-soft »

The Purebasic UDP server can unfortunately only send data when received from a client. However, there are cases where UDP server / server communication is required.
For example when devices need a fixed reply port.
This is possible with OpenNetworkConnection from Purebasic. But then unfortunately only with one connection, because you cannot use the same local port several times with the OpenNetworkConnection function.

So we are still missing the network function SendTo for CreateNetworkServer. I have made this available for all OS.

If the server is running in the thread and the SendTo functions are used outside, they must be protected with mutex.
You can find an example here: UDP Local Network Short Text Sending


Update v1.01.2
- Added IPv6 support

Update v1.02.1
- Added GetConnectionIP

Code: Select all

; *************************************************
; Comment : Network SendTo for UDP Server (All OS)
; Author  : mk-soft
; Version : v1.02.1
; Create  : 30.12.2022
; Update  : 08.01.2023
; Link    : https://www.purebasic.fr/english/viewtopic.php?t=80367

; Description
;   Socket = ServerID(Server) / ConnectionID(Client)

;-- Contants

#AF_INET = 2
CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Windows
    #AF_INET6 = 23
  CompilerCase #PB_OS_Linux
    #AF_INET6 = 10
  CompilerCase #PB_OS_MacOS
    #AF_INET6 = 30
CompilerEndSelect

;-- Structures

CompilerIf Not Defined(in_addr, #PB_Structure)
  Structure in_addr
    StructureUnion
      s_b.a[4]
      s_w.u[2]
      s_addr.l
    EndStructureUnion
  EndStructure
CompilerEndIf

CompilerIf Not Defined(in6_addr, #PB_Structure)
  Structure in6_addr
    s6_addr.a[16]
  EndStructure
CompilerEndIf

CompilerIf Not Defined(SOCKADDR, #PB_Structure)
  Structure SOCKADDR
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      sa_len.a
      sa_family.a
    CompilerElse
      sin_family.w
    CompilerEndIf
    sa_data.b[14]
  EndStructure
CompilerEndIf

CompilerIf Not Defined(SOCKADDR_IN4, #PB_Structure)
  Structure SOCKADDR_IN4
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      sin_len.a
      sin_family.a
    CompilerElse
      sin_family.w
    CompilerEndIf
    sin_port.u
    sin_addr.in_addr
    sin_zero.b[8]
  EndStructure
CompilerEndIf

CompilerIf Not Defined(SOCKADDR_IN6, #PB_Structure)
  Structure SOCKADDR_IN6
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      sin6_len.a
      sin6_family.a
    CompilerElse
      sin6_family.w
    CompilerEndIf
    sin6_port.u
    sin6_flowinfo.l
    sin6_addr.in6_addr
    sin6_scope_id.l
  EndStructure
CompilerEndIf

CompilerIf Not Defined(SOCKADDR_STORAGE, #PB_Structure)
  Structure SOCKADDR_STORAGE
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      ss_len.a
      ss_family.a
    CompilerElse
      ss_family.w
    CompilerEndIf
    ss_port.u
    ss_data.a[24]
  EndStructure
CompilerEndIf

;-- Functions

Procedure SendNetworkStringTo(Socket, IP.s, Port, Text.s, Format = #PB_UTF8, Protocol = #PB_Network_IPv4)
  Protected r1, *ip, *sendbuf, lenbuf, *RecvAddrIP4.SOCKADDR_IN4, *RecvAddrIP6.SOCKADDR_IN6
  
  Select Format
    Case #PB_Ascii
      *sendbuf = Ascii(Text)
      lenbuf = StringByteLength(Text, #PB_Ascii)
    Case #PB_UTF8, 0
      *sendbuf = UTF8(Text)
      lenbuf = StringByteLength(Text, #PB_UTF8)
    Case #PB_Unicode
      *sendbuf = @Text
      lenbuf = Len(Text)
  EndSelect
  
  *ip = Ascii(IP)
  
  Select Protocol
    Case #PB_Network_IPv4
      *RecvAddrIP4 = AllocateStructure(SOCKADDR_IN4)
      CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
        *RecvAddrIP4\sin_len = SizeOf(SOCKADDR_IN4)
      CompilerEndIf
      *RecvAddrIP4\sin_family = #AF_INET
      *RecvAddrIP4\sin_port = htons_(Port)
      *RecvAddrIP4\sin_addr\s_addr = inet_addr_(*ip)
      r1 = sendto_(socket, *sendbuf, lenbuf, 0, *RecvAddrIP4, SizeOf(SOCKADDR_IN4))
      FreeStructure(*RecvAddrIP4)
    Case #PB_Network_IPv6
      *RecvAddrIP6 = AllocateStructure(SOCKADDR_IN6)
      CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
        *RecvAddrIP6\sin6_len = SizeOf(SOCKADDR_IN6)
      CompilerEndIf
      *RecvAddrIP6\sin6_family = #AF_INET6
      *RecvAddrIP6\sin6_port = htons_(Port)
      *RecvAddrIP6\sin6_flowinfo = 0
      inet_pton_(#AF_INET6, *ip, @*RecvAddrIP6\sin6_addr)
      r1 = sendto_(socket, *sendbuf, lenbuf, 0, *RecvAddrIP6, SizeOf(SOCKADDR_IN6))
      FreeStructure(*RecvAddrIP6)
  EndSelect
  
  If *sendbuf
    FreeMemory(*sendbuf)
  EndIf
  FreeMemory(*ip)
  ProcedureReturn r1
EndProcedure

; ----

Procedure SendNetworkDataTo(Socket, IP.s, Port, *Buffer, Size, Protocol = #PB_Network_IPv4)
  Protected r1, *ip, *RecvAddrIP4.SOCKADDR_IN4, *RecvAddrIP6.SOCKADDR_IN6
  
  *ip = Ascii(IP)
  
  Select Protocol
    Case #PB_Network_IPv4
      *RecvAddrIP4 = AllocateStructure(SOCKADDR_IN4)
      CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
        *RecvAddrIP4\sin_len = SizeOf(SOCKADDR_IN4)
      CompilerEndIf
      *RecvAddrIP4\sin_family = #AF_INET
      *RecvAddrIP4\sin_port = htons_(Port)
      *RecvAddrIP4\sin_addr\s_addr = inet_addr_(*ip)
      r1 = sendto_(socket, *Buffer, Size, 0, *RecvAddrIP4, SizeOf(SOCKADDR_IN4))
      FreeStructure(*RecvAddrIP4)
    Case #PB_Network_IPv6
      *RecvAddrIP6 = AllocateStructure(SOCKADDR_IN6)
      CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
        *RecvAddrIP6\sin6_len = SizeOf(SOCKADDR_IN6)
      CompilerEndIf
      *RecvAddrIP6\sin6_family = #AF_INET6
      *RecvAddrIP6\sin6_port = htons_(Port)
      *RecvAddrIP6\sin6_flowinfo = 0
      inet_pton_(#AF_INET6, *ip, @*RecvAddrIP6\sin6_addr)
      r1 = sendto_(socket, *Buffer, Size, 0, *RecvAddrIP6, SizeOf(SOCKADDR_IN6))
      FreeStructure(*RecvAddrIP6)
  EndSelect
  
  FreeMemory(*ip)
  ProcedureReturn r1
EndProcedure

; ----

Procedure.s GetConnectionIP(Socket)
  Protected addr.SOCKADDR_STORAGE
  Protected *s4.SOCKADDR_IN4, *s6.SOCKADDR_IN6
  Protected errCode.l, len, port, *ipstr, ip.s, *error

  len = SizeOf(SOCKADDR_STORAGE)
  CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      addr\ss_len = SizeOf(SOCKADDR_STORAGE)
  CompilerEndIf
  errCode = getpeername_(Socket, addr, @len)
  If errCode >= 0
    *ipstr = AllocateMemory(256)
    Select addr\ss_family
      Case #AF_INET
        *s4 = addr
        port = ntohs_(*s4\sin_port)
        If inet_ntop_(#AF_INET, @*s4\sin_addr, *ipstr, 256);
          ip = PeekS(*ipstr, -1, #PB_Ascii)
        EndIf
      Case #AF_INET6
        *s6 = addr
        port = ntohs_(*s6\sin6_port)
        If inet_ntop_(#AF_INET6, @*s6\sin6_addr, *ipstr, 256);
          ip = PeekS(*ipstr, -1, #PB_Ascii)
        EndIf
      Default
        ip = "Unknown protocol"
    EndSelect
    FreeMemory(*ipstr)
  Else
    IP = "Unknown"
  EndIf
  ProcedureReturn ip
EndProcedure

; *************************************************
Last edited by mk-soft on Sun Jan 08, 2023 2:49 pm, edited 4 times in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
idle
Always Here
Always Here
Posts: 5903
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Network function SendTo for UDP Server (All OS)

Post by idle »

Good one thanks.
User avatar
mk-soft
Always Here
Always Here
Posts: 6248
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Network function SendTo for UDP Server (All OS)

Post by mk-soft »

Update v1.01.2
- Added IPv6 support

Had to adapt some structures. MacOS still has the length in sockaddr_in and sockaddr_in6. So at first the sending still failed.
:wink:
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Post Reply