Wrappers to do full sendNetworkData and ReceiveNetworkData

Share your advanced PureBasic knowledge/code with the community.
User avatar
idle
Always Here
Always Here
Posts: 5884
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by idle »

Wrappers to do full sendNetworkData and ReceiveNetworkData
Might need fix ups for error constants and getting the errno for linux and osx


SendNetworkDataEX(clientId,*buffer,len,timeout=15000,mutex=0,*error.Integer)
Sends the whole buffer and returns the number of bytes sent or 0 on error

ReceiveNetworkDataEx(clientId,len,timeout=15000,mutex=0,*error.Integer=0)
Returns a pointer to the *buffer or 0 on error

Code: Select all

  ;-Extra functions 
#PB_Network_Error_Fatal = -1 
#PB_Network_Error_timeout = -2 
#PB_Network_Error_Dropped = -3 
#PB_Network_Error_Memory = -4 

CompilerIf #PB_Compiler_OS = #PB_OS_Linux 
   CompilerIf #PB_Compiler_Backend = #PB_Backend_Asm 
     ImportC "-lc" 
       __errno_location() 
     EndImport   
   CompilerEndIf
CompilerEndIf   
  
Procedure NetworkErrorContinue(ID,val=0) 
  Protected ret,error.l
  
  #WSA_IO_INCOMPLETE = 996
  #WSA_IO_PENDING = 997
  
   CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows 
      #WSAEINTR = 10004
      #WSAEMFILE = 10024
      #WSAEWOULDBLOCK = 10035
      #WSAEINPROGRESS = 10036
      #WSAEALREADY = 10037
    CompilerCase #PB_OS_Linux
      #WSAEINTR = 4         ; EINTR 
      #WSAEMFILE = 17       ; ENOFILE 
      #WSAEWOULDBLOCK = 11  ; Eagain  
      #WSAEINPROGRESS = 115 ; EINPROGRESS
      #WSAEALREADY = 114    ; EALREADY 
    CompilerCase #PB_OS_MacOS
      #WSAEINTR = 4         ; EINTR 
      #WSAEMFILE = 24       ; EMFILE 
      #WSAEWOULDBLOCK = 35  ; EWOULDBLOCK = EAGAIN  
      #WSAEINPROGRESS = 36  ; EINPROGRESS
      #WSAEALREADY = 37     ; EALREADY 
  CompilerEndSelect
    
  #TLS_WANT_POLLIN  = -2
  #TLS_WANT_POLLOUT = -3
    
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    error = WSAGetLastError_()
  CompilerElse 
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C
      !#include "errno.h"
      !extern int errno;
      !v_error=errno;
    CompilerElse
      error = PeekL(__errno_location()) 
    CompilerEndIf 
  CompilerEndIf
  
  If val = #TLS_WANT_POLLIN
     Debug "#TLS_WANT_POLLIN"
    ProcedureReturn  1
  EndIf   
  If val = #TLS_WANT_POLLOUT  
     Debug "#TLS_WANT_POLLOUT"
    ProcedureReturn 1 
  EndIf   
  
  Select error 
    Case 0    ;shouldn't really get a case of 0 it means the errors been reset  
       ret = 0     
       Debug "None"
    Case  #WSAEWOULDBLOCK  
      ret = 1 
      Debug "#WSAEWOULDBLOCK"
    Case  #WSAEINPROGRESS   
      Debug "#WSAEINPROGRESS"
      ret = 1  
    Case  #WSAEALREADY  
      Debug "#WSAEALREADY"
      ret = 1  
    Case  #WSA_IO_INCOMPLETE
      Debug "#WSA_IO_INCOMPLETE"
      ret =1   
    Case  #WSA_IO_PENDING
      Debug "#WSA_IO_PENDING"
      ret = 1  
    Case  #WSAEMFILE 
      ret =1 
      Debug "#WSAEMFILE"
    Default 
      Debug error   
  EndSelect   
  
  ProcedureReturn ret 
  
EndProcedure  

Procedure ReceiveNetworkDataEx(clientId,len,timeout=15000,mutex=0,*error.Integer=0) 
  
  Protected result,recived,recvTimeout
  
  If len > 0 
    Protected *buffer = AllocateMemory(len)
    If *buffer 
      
      recvTimeout=ElapsedMilliseconds()+timeout   
      
      Repeat
        If result > 0
           *buffer = ReAllocateMemory(*buffer, recived + len) 
        EndIf 
        If *buffer 
          If mutex 
            Repeat 
              If TryLockMutex(mutex)
                Result = ReceiveNetworkData(clientId,*buffer+recived, len) 
                If result < 0 
                  If NetworkErrorContinue(clientId,result) 
                    Delay(1)
                  Else 
                    UnlockMutex(mutex)
                    FreeMemory(*buffer)
                    If *error 
                      *error\i = #PB_Network_Error_Fatal
                    EndIf   
                    ProcedureReturn 0
                  EndIf 
                EndIf   
                UnlockMutex(mutex) 
                Break 
              Else 
                Delay(10)
              EndIf   
            Until  ElapsedMilliseconds() > recvTimeout  
          Else       
            Result = ReceiveNetworkData(clientId,*buffer+recived, len)
            If result < 0 
              If NetworkErrorContinue(clientId,result) 
                Delay(1)
                Continue 
              Else 
                FreeMemory(*buffer)
                If *error 
                  *error\i = #PB_Network_Error_Fatal
                EndIf   
                ProcedureReturn 0
              EndIf 
            EndIf   
          EndIf   
          
          If result > 0 
            recived+result  
            recvTimeout = ElapsedMilliseconds() + timeout
          ElseIf result = 0 
            FreeMemory(*buffer)
            If *error 
              *error\i = #PB_Network_Error_Dropped 
            EndIf   
            ProcedureReturn 0
          EndIf   
        Else 
          If *error 
            *error\i = #PB_Network_Error_Memory 
          EndIf   
          ProcedureReturn 0
        EndIf   
        
        If ElapsedMilliseconds() > recvTimeout    
          FreeMemory(*buffer)
          If *error 
            *error\i = #PB_Network_Error_timeout 
          EndIf   
          ProcedureReturn 0
        EndIf 
        
      Until result <> len   
      
      ProcedureReturn *buffer
      
    EndIf 
  EndIf 
  
EndProcedure   

Procedure SendNetworkDataEX(clientId,*buffer,len,timeout=15000,mutex=0,*error.Integer=0) 
  
  Protected  totalSent,tryLen,sendLen,sendTimeout
  
  sendTimeout = ElapsedMilliseconds() + timeout
  Repeat
    
    tryLen = len - totalSent
    If tryLen > len 
      tryLen = len 
    EndIf
    If mutex 
      Repeat 
        If TryLockMutex(mutex)  
          sendLen = SendNetworkData(clientId, *Buffer+totalSent,tryLen)
          If sendLen < 0 
            If NetworkErrorContinue(clientId,sendLen) 
              Delay(1) 
            Else 
              If *error 
                *error\i = #PB_Network_Error_Fatal
              EndIf   
              UnlockMutex(mutex)
              ProcedureReturn 0
            EndIf 
          EndIf 
          UnlockMutex(mutex) 
          Break 
        Else 
          Delay(10)
        EndIf 
      Until ElapsedMilliseconds() > sendTimeout 
    Else 
      sendLen = SendNetworkData(clientId, *Buffer+totalSent,tryLen)
      If sendLen < 0 
        If NetworkErrorContinue(clientId,sendLen) 
          Delay(1) 
        Else 
          If *error 
            *error\i = #PB_Network_Error_Fatal
          EndIf   
          ProcedureReturn 0
        EndIf 
      EndIf 
    EndIf   
    
    If sendLen > 0
      totalSent + sendLen
      sendLen = 0 
      sendTimeout = ElapsedMilliseconds() + timeout
    ElseIf sendLen = 0 
      If *error 
        *error\i = #PB_Network_Error_Dropped  
      EndIf   
      ProcedureReturn 0 
    EndIf 
    
    If ElapsedMilliseconds() > sendTimeout
      If *error 
        *error\i = #PB_Network_Error_timeout 
      EndIf   
      ProcedureReturn 0
    EndIf 
    
    Delay(1) 
    
  Until totalSent >= len 
  
  ProcedureReturn totalSent 
  
EndProcedure   


Quin
Addict
Addict
Posts: 1132
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by Quin »

Thanks idle, you posted this at a perfect time for me, when I have time off and am working on refactoring an old networking application. Thanks so much, I'll let you know how it works!
User avatar
idle
Always Here
Always Here
Posts: 5884
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by idle »

updated wasn't trapping the TLS errors
rearranged in the hope to eliminate the odd socket error being reset.
Quin
Addict
Addict
Posts: 1132
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by Quin »

Hey Idle,
What's the right way of constructing strings to be sent with SendNetworkDataEx? I've tried using Res = UTF8(MyString), Res, StringByteLength(Res), but this garbles it. My receiving code works perfectly with PeekS and #PB_UTF8 when i connect to it with telnet, but sending with SendNetworkDataEx is broken.
Thanks!
User avatar
idle
Always Here
Always Here
Posts: 5884
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by idle »

If you convert a string to utf8 use memorysize on the pointer. *str = utf8(string) and the length is memorysize(*str)
If that's not working then I've made a mistake in the post.
Quin
Addict
Addict
Posts: 1132
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by Quin »

Wow, that was fast, here I was thinking I may be able to drift off before you replied :D.
I got it using what you suggested, thanks so much for this!
User avatar
idle
Always Here
Always Here
Posts: 5884
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by idle »

Good timing, I was just checking in before I sign off for the night.
Quin
Addict
Addict
Posts: 1132
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by Quin »

If my server doesn't exist and/or goes down, my client application will simply crash, saying the specified connection ID is null. Is there a way to check for and prevent this? Will something as simple as If ClientID : EndIf around the send/receive fix it?
User avatar
HeX0R
Addict
Addict
Posts: 1198
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by HeX0R »

If the server doesn't exist, OpenNetworkConnection() will fail, so how did you end up in the sending/receiving procedures?
I guess the second scenario (server goes down while transmitting) might lead to trouble, when you use idles procedures (wrong) in a thread.
Because then I could imagine, that the main thread got its #PB_NetworkEvent_Disconnect signal, while the thread is still inside idles sending procedure.
When using threads, you have to take care about correct mutex handling
Quin
Addict
Addict
Posts: 1132
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by Quin »

HeX0R wrote: Mon Mar 24, 2025 7:21 pm If the server doesn't exist, OpenNetworkConnection() will fail, so how did you end up in the sending/receiving procedures?
I guess the second scenario (server goes down while transmitting) might lead to trouble, when you use idles procedures (wrong) in a thread.
Because then I could imagine, that the main thread got its #PB_NetworkEvent_Disconnect signal, while the thread is still inside idles sending procedure.
When using threads, you have to take care about correct mutex handling
Yeah, I'm writing a UI-based application that tries to call these procedures from a thread. What's the correct way to check if the connection exists before calling it?
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by tatanas »

Try this Quin :

Code: Select all

Procedure Is_Socket_Available(SocketHandle)

	CompilerIf #PB_Compiler_OS = #PB_OS_Windows
		Protected name.SOCKADDR
		Protected namelen = SizeOf(SOCKADDR)
		
		If getpeername_(SocketHandle, name, @namelen) <> 0 ; on a une erreur
			Debug WSAGetLastError_()
			ProcedureReturn 0
		Else
			ProcedureReturn 1
		EndIf
		
	CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux	
		Protected name.sockaddr_in
		Protected namelen = SizeOf(sockaddr_in)
		
		If getpeername_(SocketHandle, @name, @namelen) <> 0 ; on a une erreur
			ProcedureReturn 0
		Else
			ProcedureReturn 1
		EndIf
	CompilerEndIf
EndProcedure
Windows 10 Pro x64
PureBasic 6.20 x64
Quin
Addict
Addict
Posts: 1132
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by Quin »

tatanas wrote: Tue Mar 25, 2025 1:34 pm Try this Quin :

Code: Select all

Procedure Is_Socket_Available(SocketHandle)

	CompilerIf #PB_Compiler_OS = #PB_OS_Windows
		Protected name.SOCKADDR
		Protected namelen = SizeOf(SOCKADDR)
		
		If getpeername_(SocketHandle, name, @namelen) <> 0 ; on a une erreur
			Debug WSAGetLastError_()
			ProcedureReturn 0
		Else
			ProcedureReturn 1
		EndIf
		
	CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux	
		Protected name.sockaddr_in
		Protected namelen = SizeOf(sockaddr_in)
		
		If getpeername_(SocketHandle, @name, @namelen) <> 0 ; on a une erreur
			ProcedureReturn 0
		Else
			ProcedureReturn 1
		EndIf
	CompilerEndIf
EndProcedure
This looks incredible, thanks! I'll try after my lectures today.
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by tatanas »

I asked the same question to Copilot and it seems most of the time the best method to test a socket availabilty is to send a empty message.
The problem with SendNetworkData() is if the ClientID (socket) is not accessible, it crashes the application (invalid memory access) instead of returning an "clean" error.
So maybe the send_() function with an empty message and handling the return error could be better than the previous code ?

Code: Select all

Procedure Is_SocketAvailable(SocketHandle)
	Protected ret

	CompilerIf #PB_Compiler_OS = #PB_OS_Linux
		#ENETDOWN		= 100
		#EACCES			= 13
		#EINTR			= 4
		#EINPROGRESS	= 115
		#EFAULT			= 14
		#ENETRESET		= 102
		#ENOBUFS			= 105
		#ENOTCONN		= 107
		#ENOTSOCK		= 88
		#EOPNOTSUPP		= 95
		#ESHUTDOWN		= 108
		#EWOULDBLOCK	= 11
		#EMSGSIZE		= 90
		#EHOSTUNREACH	= 113
		#EINVAL			= 22
		#ECONNABORTED	= 103
		#ECONNRESET		= 104
		#ETIMEDOUT		= 110
	CompilerEndIf

	ret = send_(SocketHandle, 0, 0, 0)

	CompilerIf #PB_Compiler_OS = #PB_OS_Windows
		If ret = 0
	 		ProcedureReturn 1
		Else
			Select WSAGetLastError_()
				Case #WSANOTINITIALISED
					Debug "#WSANOTINITIALISED"
				Case #WSAENETDOWN
					Debug "#WSAENETDOWN"
				Case #WSAEACCES
					Debug "#WSAEACCES"
				Case #WSAEINTR
					Debug "#WSAEINTR"
				Case #WSAEINPROGRESS
					Debug "#WSAEINPROGRESS"
				Case #WSAEFAULT
					Debug "#WSAEFAULT"
				Case #WSAENETRESET
					Debug "#WSAENETRESET"
				Case #WSAENOBUFS
					Debug "#WSAENOBUFS"
				Case #WSAENOTCONN
					Debug "#WSAENOTCONN"
				Case #WSAENOTSOCK
					Debug "#WSAENOTSOCK"
				Case #WSAEOPNOTSUPP
					Debug "#WSAEOPNOTSUPP"
				Case #WSAESHUTDOWN
					Debug "#WSAESHUTDOWN"
				Case #WSAEWOULDBLOCK
					Debug "#WSAEWOULDBLOCK"
				Case #WSAEMSGSIZE
					Debug "#WSAEMSGSIZE"
				Case #WSAEHOSTUNREACH
					Debug "#WSAEHOSTUNREACH"
				Case #WSAEINVAL
					Debug "#WSAEINVAL"
				Case #WSAECONNABORTED
					Debug "#WSAECONNABORTED"
				Case #WSAECONNRESET
					Debug "#WSAECONNRESET"
				Case #WSAETIMEDOUT
					Debug "#WSAETIMEDOUT"
			EndSelect
		EndIf
	CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
		If ret = -1
			Select PeekL(errno_location())
				Case #ENETDOWN
					Debug "#ENETDOWN"
				Case #EACCES
					Debug "#EACCES"
				Case #EINTR
					Debug "#EINTR"
				Case #EINPROGRESS
					Debug "#EINPROGRESS"
				Case #EFAULT
					Debug "#EFAULT"
				Case #ENETRESET
					Debug "#ENETRESET"
				Case #ENOBUFS
					Debug "#ENOBUFS"
				Case #ENOTCONN
					Debug "#ENOTCONN"
				Case #ENOTSOCK
					Debug "#ENOTSOCK"
				Case #EOPNOTSUPP
					Debug "#EOPNOTSUPP"
				Case #ESHUTDOWN
					Debug "#ESHUTDOWN"
				Case #EWOULDBLOCK
					Debug "#EWOULDBLOCK"
				Case #EMSGSIZE
					Debug "#EMSGSIZE"
				Case #EHOSTUNREACH
					Debug "#EHOSTUNREACH"
				Case #EINVAL
					Debug "#EINVAL"
				Case #ECONNABORTED
					Debug "#ECONNABORTED"
				Case #ECONNRESET
					Debug "#ECONNRESET"
				Case #ETIMEDOUT
					Debug "#ETIMEDOUT"
			EndSelect
			ProcedureReturn 0
		Else
			ProcedureReturn 1
		EndIf
	CompilerEndIf

	ProcedureReturn 0
EndProcedure
What is the best method ?
Windows 10 Pro x64
PureBasic 6.20 x64
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by tatanas »

Question for idle :

How would you handle with SendNetworkDataEx() a message > 65536 bytes ?
Windows 10 Pro x64
PureBasic 6.20 x64
User avatar
idle
Always Here
Always Here
Posts: 5884
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Wrappers to do full sendNetworkData and ReceiveNetworkData

Post by idle »

It sends the length specified and will either return the amount sent on success or 0 and set the error variable.
I don't understand why your having trouble with the connection, it will just fail and return the error
I am using the functions in the server for both server and client via the reverse proxy and its not crashing
Post Reply