Page 1 of 1

Network function SendTo for UDP Server (All OS)

Posted: Fri Dec 30, 2022 8:01 pm
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

; *************************************************

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

Posted: Fri Dec 30, 2022 8:33 pm
by idle
Good one thanks.

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

Posted: Sun Jan 01, 2023 6:33 pm
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: