The woes of PureBasic's networking library

Everything else that doesn't fall into one of the other PB categories.
Quin
Addict
Addict
Posts: 1133
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

The woes of PureBasic's networking library

Post by Quin »

Hi,
First, I want to say I deeply respect the work that’s gone into PureBasic. It’s an incredible tool that’s stood the test of time, and I really admire what Fred has built, especially as a solo developer. It handles many things very well. Even when some features are missing (like setting/clearing selection in StringGadgets), there are often easy workarounds via the API, so that’s not a huge issue.
That said, after over a year of working with the networking library, I’ve found it to be one of the weakest parts of PB. It feels like a leaky abstraction over sockets, and I’ve run into many challenges:
  • The API is quite different from how most socket libraries do it, and it doesn’t use select, so you have to Delay(1) in your main loop, which can introduce latency.
  • ReceiveNetworkData returns -2 in some cases, namely when trying to make it work with an nginx proxy for TLS without needing to implement key rotation with UseNetworkTLS, although the docs only mention -1.
  • There’s no NetworkError() function, though I know Fred has expressed interest in adding one.
  • There’s also no way to check if a client ID is valid (something like IsSocket() or IsNetworkConnection()), which can lead to crashes from invalid IDs.
  • and so on...
I understand that PureBasic isn't open source, and development is mostly in Fred’s hands, so timelines for fixes are unclear. That makes it hard to rely on the networking library for anything production-level.
In my own case, I’ve been maintaining a peer-to-peer chat app for a small group of blind friends (around 10 users). After literally a year of trying to work around the networking libraries' limitations, I ended up rewriting the server part in Go, PB just wasn’t viable for it and kept crashing in weird places due to invalid client IDs in some rare cases and I couldn't GDB it thanks to -ds being broken.
So while I think PB is excellent for many things (especially GUI and quick tooling), I can’t currently recommend it for projects that heavily depend on networking. I truly hope this area sees some love in future releases, and I’d love to return to using PB for this kind of work if that happens.
User avatar
idle
Always Here
Always Here
Posts: 5896
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: The woes of PureBasic's networking library

Post by idle »

writing network servers can be endlessly frustrating when you don't actually know what the socket configuration is, and what works on loopback and Lan often doesn't work quite so well on Wan with higher latencies and that make is very hard to debug when you have no idea what the error is or where the bug is in your code and it get harder when it's threaded.

We ideally need to be able to set socket options, like keep alive, reuse and tcp no delay
we also need a network error function, or as I've done NetworkContinue which is simpler to use, you don't really need to know what the fatal error was, you only need to know if the error is recoverable, so you can delay and continue and that can all be wrapped in send and receive functions which either succeed or fail and return the error.

Currently the documentation causes some confusion as a socket returns either
0: connection was closed. (this isn't mentioned)
n bytes sent or received
-1 on error (which would have you bail out when 99% of the time it's actually recoverable)
And additionally since 6.20 we now have
#TLS_want_pollin = -2 (comes from the libre TLS sockets)
#TLS_want_ pollout = -3
both are recoverable meaning delay and try again

Thing is we can't really expect Fred to put on a pink tutu and wave a magic wand, it needs a community coordinated effort with real world tests not just over LAN and that takes time. Atomic web server for instance despite it's complexity runs flawlessly but it's taken a long time to get there and was the actual testbed for getting TLS introduced to PB via Hexors TLS wrapper along with infratec's help and myself debugging the TLS wrapper and then badgering Fred to add it natively.

These are the functions I'm using in atomic web server to do send and receives they are both used with the reverse proxy and cause no issues though I haven't tested the mutex branches as they're not needed due to the threading model of the server.

Code: Select all


CompilerIf #PB_Compiler_OS <> #PB_OS_Windows  
  
  #SOL_SOCKET = 1
  #SO_KEEPALIVE = 8
  #IPPROTO_TCP = 6
  #TCP_NODELAY = 1
  #SO_LINGER = 13 
  
CompilerEndIf   

Procedure KeepAlive(ID,set.l=#True) 
  Protected option.l,oplen.l=4 
  If setsockopt_(ID,#SOL_SOCKET,#SO_KEEPALIVE,@set,oplen) = 0 
    If getsockopt_(ID,#SOL_SOCKET,#SO_KEEPALIVE,@option,@oplen ) = 0 
      ProcedureReturn option    
    EndIf 
    ProcedureReturn -1     
  EndIf   
  
EndProcedure    

Procedure TCPNoDelay(ID,set.l=#True)  
  Protected option.l,oplen.l=4 
  If setsockopt_(ID,#IPPROTO_TCP,#TCP_NODELAY,@set,oplen) = 0 
    If getsockopt_(ID,#IPPROTO_TCP,#TCP_NODELAY,@option,@oplen ) = 0 
      ProcedureReturn option    
    EndIf 
    ProcedureReturn -1     
  EndIf  
EndProcedure   

;-Error codes  
#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
	ImportC "-lc"
		__errno_location()
	EndImport
CompilerEndIf

Procedure.i NetworkErrorContinue(ID.i, Val.i = 0)
  Protected Ret.i, 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 
    Error = PeekL(__errno_location())
  CompilerEndIf
  If Val = #TLS_WANT_POLLIN 
    ProcedureReturn  #True
  EndIf
  If Val = #TLS_WANT_POLLOUT 
    ProcedureReturn #True 
  EndIf
  Select Error
    Case 0 
      Ret = 0
    Case #WSAEWOULDBLOCK
      Ret = 1
    Case #WSAEINPROGRESS
      Ret = 1
    Case #WSAEALREADY 
      Ret = 1
    Case #WSA_IO_INCOMPLETE 
      Ret =1
    Case #WSA_IO_PENDING 
      Ret = 1
    Case #WSAEMFILE 
      Ret =1
  EndSelect
  ProcedureReturn Ret
EndProcedure

;returns the buffer or 0 on error pass in the address of a error variable @error if you want to check it
Procedure Atomic_Server_ReceiveNetworkDataEx(clientId,len,timeout=15000,mutex=0,*error.Integer=0) 
  
  Protected result,recived,recvTimeout,tlen,bfirst=1
  
  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 Atomic_Server_NetworkErrorContinue(clientId,result) 
                    Delay(10)
                  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 Atomic_Server_NetworkErrorContinue(clientId,result) 
                Delay(10)
                Continue 
              Else 
                FreeMemory(*buffer)
                If *error 
                  *error\i = #PB_Network_Error_Fatal
                EndIf   
                Delay(10)
                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 
        Delay(0) 
      Until result <> len   
      
      ProcedureReturn *buffer
      
    EndIf 
  EndIf 
  
EndProcedure   

;returns the total sent or 0 on errror pass in the address of a varibale @error to get the error 
Procedure Atomic_Server_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 Atomic_Server_NetworkErrorContinue(clientId,sendLen) 
              Delay(10) 
            Else 
            If *error 
               *error\i = #PB_Network_Error_Fatal
            EndIf   
             Debug Str(totalsent) + " " + Str(trylen) + " " + Str(len) 
             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 Atomic_Server_NetworkErrorContinue(clientId,sendLen) 
          Delay(10) 
        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   
Fred
Administrator
Administrator
Posts: 18220
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: The woes of PureBasic's networking library

Post by Fred »

@Quin, I hear you, please provide some code showing the issues and suggestions as well to make it better (new command name, flags etc.)
Quin
Addict
Addict
Posts: 1133
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: The woes of PureBasic's networking library

Post by Quin »

Hi Fred,
Thanks for your response! I'll draft up a document showing some improvements to the networking library over the next few days and post it. Just figured I'd reply here so you know you're not being ignored :D
Thanks for your incredible work and all your responses!
User avatar
mk-soft
Always Here
Always Here
Posts: 6245
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: The woes of PureBasic's networking library

Post by mk-soft »

I have few problems with my network functions, but I can intercept them.
This also means that you have to manage the connected clients properly yourself.

Fred will provide us with the NetworkError function.
Until then, the idle shows where you can solve this.

So.
The Network lib works and I have been using it for a long time. But you can always improve something ...
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
Quin
Addict
Addict
Posts: 1133
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: The woes of PureBasic's networking library

Post by Quin »

Truth be told my largest issue is the lack of use of select, necessitating a Delay(1) in your net loop. "Oh but it's just one ms!" Yeah, one MS on every loop iteration. Receive a connection? 1 ms. Receive the beginning of a packet? 1 ms. Receive the next chunk? 1 MS. And so on...
Perhaps a WaitNetworkEvent([Timeout]) function or similar would be nice?
Also, like I mentioned above, something like a IsConnection() or IsClientID() function would be great. I had a case in my app where CloseNetworkConnection() was getting called twice, and I had to step through the code and try and figure it out or my code would crash. Granted, this *was* a bug in my code, but IMO it's a bug I should be able to get around by checking if the connection is valid with a bulletproof function like most other PB libraries have.
Fred
Administrator
Administrator
Posts: 18220
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: The woes of PureBasic's networking library

Post by Fred »

Your 1 ms theory is wrong, if there is something in queue, there is no delay (obvsiously), so you only wait 1 ms if the server is actually idle. That said, I don't think it should hard to have a blocking select. None blocking call allows to handle multiple connection in the same thread, which can be evry useful if you have a lot, as every thread consume it's own memory for stack, add pressure on the scheduler etc.

Also the problem with bulletproof function is it takes time because you need to validate the value across a list or a map. So if you add these to workaround a bug in your code, you will loose a lot of time, may be more than the 1ms you will save here and here with a blocking call :P

As you said, it was a bug in your code, so it's need to be fixed and these functions aren't really useful.
User avatar
mk-soft
Always Here
Always Here
Posts: 6245
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: The woes of PureBasic's networking library

Post by mk-soft »

Server Example for UTF8 Text Receive with end character #LF$ or #CR$ ...

Code: Select all

;-TOP by mk-soft, v1.01.0, 06.06.2025

CompilerIf Not #PB_Compiler_Thread
  CompilerError "Use Compiler Option Threadsafe!"
CompilerEndIf

Enumeration Windows
  #Main
EndEnumeration

Enumeration MenuBar
  #MainMenu
EndEnumeration

Enumeration MenuItems
  #MainMenuStartServer
  #MainMenuStopServer
  #MainMenuExit
EndEnumeration

Enumeration Gadgets
  #MainTraceList
EndEnumeration

Enumeration StatusBar
  #MainStatusBar
EndEnumeration

; ----

Enumeration CustomEvent #PB_Event_FirstCustomValue
  #MyEvent_Trace
EndEnumeration

; ---- String Helper ----
  
Procedure AllocateString(String.s) ; Result = Pointer
  Protected *mem.string = AllocateStructure(String)
  If *mem
    *mem\s = String
  EndIf
  ProcedureReturn *mem
EndProcedure

Procedure.s FreeString(*mem.string) ; Result String
  Protected r1.s
  If *mem
    r1 = *mem\s
    FreeStructure(*mem)
  EndIf
  ProcedureReturn r1
EndProcedure

; ----

Procedure DoEventTrace()
  Protected *data, cnt
  *data = EventData()
  If *data
    cnt = CountGadgetItems(#MainTraceList)
    AddGadgetItem(#MainTraceList, -1, FreeString(*data))
    SetGadgetState(#MainTraceList, cnt)
    SetGadgetState(#MainTraceList, -1)
    If cnt >= 1000
      RemoveGadgetItem(#MainTraceList, 0)
    EndIf
  EndIf
EndProcedure : BindEvent(#MyEvent_Trace, @DoEventTrace())

Procedure __Trace(Info.s, Modul.s, Proc.s, Line)
  Protected Text.s
  If Modul = ""
    Modul = "Main"
  EndIf
  Text = FormatDate("[%HH:%II:%SS] ", Date())
  Text + "[Module " + Modul + "; Proc " + Proc + "; Line " + Line + "] " + Info
  PostEvent(#MyEvent_Trace, 0, 0, 0, AllocateString(Text))  
EndProcedure

Macro Trace(Info, Modul = #PB_Compiler_Module, Proc = #PB_Compiler_Procedure, Line = #PB_Compiler_Line)
  __Trace(Info, Modul, Proc, Line)
EndMacro

;-- Begin Server

Structure udtServer
  ThreadID.i
  Exit.i
  ; Data
  ServerID.i
  Port.i
  *Buffer
EndStructure

Structure udtClient
  Connection.i
  IP.i
  ConnectionDateTime.i
  LastTime.i
  ; Data
  ReceiveData.s
EndStructure

Global NewMap Client.udtClient()
Global Server.udtServer
Server\Port = 6023

Procedure thWork(*Data.udtServer)
  Protected Count, Connection, Len, Pos, Pos2, Text.s, Time, LastTime
  With *Data
    Trace("Init Thread and Server")
    Repeat
      \ServerID = CreateNetworkServer(#PB_Any, \Port)
      If \ServerID
        Break
      EndIf
      Count + 1
      If Count > 3
        Trace("Error Create Server")
        \Exit = 0
        \ThreadID = 0
        ProcedureReturn 0
      EndIf
      Delay(1000)
    ForEver
    
    \Buffer = AllocateMemory(8192)
    
    Trace("Server Running ...")
    
    LastTime = ElapsedMilliseconds()
    
    Repeat
      Select NetworkServerEvent()
        Case #PB_NetworkEvent_Connect
          Connection = EventClient()
          If FindMapElement(Client(), Str(Connection))
            ; Invaild client data, Should never happen
            DeleteMapElement(Client())
          EndIf
          AddMapElement(Client(), Str(Connection))
          Client()\Connection = Connection
          Client()\IP = 0;GetClientIP(Connection)
          Client()\ConnectionDateTime = Date()
          Client()\LastTime = ElapsedMilliseconds()
          Trace("Client Connected ID " + Client()\Connection)
          
        Case #PB_NetworkEvent_Disconnect
          Connection = EventClient()
          If FindMapElement(Client(), Str(Connection))
            ; ToDo Release Client
            Trace("Client Disconnected ID " + Client()\Connection)
            DeleteMapElement(Client())
          EndIf
          
        Case #PB_NetworkEvent_Data
          Connection = EventClient()
          If Not FindMapElement(Client(), Str(Connection))
            Trace("Invaild Client Data. Should never happen!")
            CloseNetworkConnection(Connection)
          Else
            Len = ReceiveNetworkData(Connection, \Buffer, 8192)
            If len > 0
              Client()\ReceiveData + PeekS(\Buffer, len, #PB_UTF8)
            EndIf
            ; Split Receive Text
            Repeat
              pos = FindString(Client()\ReceiveData, #LF$)
              If Not Pos
                pos = FindString(Client()\ReceiveData, #CR$)
              EndIf
              If Not Pos
                Break
              EndIf
              Text = Left(Client()\ReceiveData, pos - 1)
              Client()\ReceiveData = Mid(Client()\ReceiveData, pos + 1)
              ; ToDo Part
              Trace("Data Conntection ID " + Client()\Connection)
              Trace("Text: " + Text)
            ForEver
          EndIf
          
        Case #PB_NetworkEvent_None
          ; Check Client Timeout No Data
          Time = ElapsedMilliseconds()
          If (Time - LastTime) > 1000
            LastTime = Time
            ForEach Client()
              If (Time - Client()\LastTime) > 30 * 60 * 1000
                Trace("Client Data Timeout Connection ID " + Client()\Connection
                CloseNetworkConnection(Client()\Connection)
                ; ToDo release Client Data
                DeleteMapElement(Client())
              Else
                
              EndIf
            Next
          EndIf
          Delay(10)
          
      EndSelect
      
    Until *Data\Exit
    
    Trace("Stop Server")
    ForEach Client()
      CloseNetworkConnection(Client()\Connection)
    Next
    CloseNetworkServer(\ServerID)
    FreeMemory(\Buffer)
    
    Trace("Thread Done.")
    \Exit = #False
    \ThreadID = 0
  EndWith
  
EndProcedure

;-- End Server

; ----

Procedure UpdateWindow()
  Protected dx, dy
  dx = WindowWidth(#Main)
  dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
  ; Resize gadgets
  ResizeGadget(#MainTraceList, 0, 0, dx, dy)
EndProcedure

Procedure Main()
  Protected dx, dy
  Protected ExitTime
  
  #MainStyle = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget
  
  If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, 800, 600, "Win Server Example" , #MainStyle)
    ; Menu
    CreateMenu(#MainMenu, WindowID(#Main))
    MenuTitle("&File")
    MenuItem(#MainMenuStartServer, "Start Server")
    MenuItem(#MainMenuStopServer, "Stop Server")
    MenuBar()
    MenuItem(#MainMenuExit, "E&xit")
    
    ; StatusBar
    CreateStatusBar(#MainStatusBar, WindowID(#Main))
    AddStatusBarField(#PB_Ignore)
    
    ; Gadgets
    dx = WindowWidth(#Main)
    dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
    ListViewGadget(#MainTraceList, 0, 0, dx, dy)
    
    ; Bind Events
    BindEvent(#PB_Event_SizeWindow, @UpdateWindow(), #Main)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Select EventWindow()
            Case #Main
              Break
              
          EndSelect
          
        Case #PB_Event_Menu
          Select EventMenu()
            CompilerIf #PB_Compiler_OS = #PB_OS_MacOS   
              Case #PB_Menu_About
                
              Case #PB_Menu_Preferences
                
              Case #PB_Menu_Quit
                PostEvent(#PB_Event_CloseWindow, #Main, #Null)
                
            CompilerEndIf
            
          Case #MainMenuExit
            PostEvent(#PB_Event_CloseWindow, #Main, #Null)
            
          Case #MainMenuStartServer
            If Not IsThread(Server\ThreadID)
              Server\ThreadID = CreateThread(@thWork(), @Server)
            EndIf
            
          Case #MainMenuStopServer
            If IsThread(Server\ThreadID)
              Server\Exit = #True
            EndIf
          EndSelect
          
        Case #PB_Event_Gadget
          Select EventGadget()
              
          EndSelect
          
      EndSelect
    ForEver
    
    ;-- ExitProgram
    If IsThread(Server\ThreadID)
      Server\Exit = #True
      ExitTime = ElapsedMilliseconds()
      Repeat
        WaitWindowEvent(100)
        If ElapsedMilliseconds() - ExitTime >= 2000
          Break
        EndIf
      ForEver
    EndIf
    If IsThread(Server\ThreadID)
      KillThread(Server\ThreadID)
    EndIf
  EndIf
  
EndProcedure : Main()
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_Russian
Addict
Addict
Posts: 1535
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: The woes of PureBasic's networking library

Post by User_Russian »

Hello Fred.
If you decide to improve the network library, please consider my request for non-blocking sockets viewtopic.php?p=402824#p402824
This is necessary for multiple TCP connections without waiting for a connection.
The function OpenNetworkConnection() waits for TCP connection, which is why the code works very slowly when there are many unavailable addresses. Create hundreds of threads and call OpenNetworkConnection() this is a very dirty method.

I see the implementation of non-blocking sockets like this.
Need to add a mode #PB_Network_NoBlocking. Function OpenNetworkConnection() should complete very quickly without waiting for a connection. Parameter TimeOut sets the maximum time it takes to establish a connection.
If the time has expired and the connection is not established, the function NetworkClientEvent() must return #PB_NetworkEvent_TimeOut or #PB_NetworkEvent_Connect if the connection is established.

These function would also be useful viewtopic.php?t=55403

Thank you!
swhite
Enthusiast
Enthusiast
Posts: 798
Joined: Thu May 21, 2009 6:56 pm

Re: The woes of PureBasic's networking library

Post by swhite »

Hi

I use the Networking features a lot and the ability to detect whether the connection is valid would be very useful. I have micro services that communicate via TCP/IP and trying to prevent crashes when network connections become unavailable for reasons outside my application is difficult. If there was some way to verify the connection or at least not crash the application. A network Error function would be helpful in this case.

Thanks,
Simon
Simon White
dCipher Computing
Quin
Addict
Addict
Posts: 1133
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: The woes of PureBasic's networking library

Post by Quin »

swhite wrote: Mon Jun 09, 2025 2:37 pm Hi

I use the Networking features a lot and the ability to detect whether the connection is valid would be very useful. I have micro services that communicate via TCP/IP and trying to prevent crashes when network connections become unavailable for reasons outside my application is difficult. If there was some way to verify the connection or at least not crash the application. A network Error function would be helpful in this case.

Thanks,
Simon
Thank you Simon, you put into words what I was struggling to.
The invalid client ID was a bug in my code, but you can get them for reasons that are entirely outside of your control. For example, if you get an RST, or if one of your middleware devices has a NAT mapping expire, you'll get invalid client iDs. Maybe an IsConnection function wouldn't be great for this because you have to loop over a list/map of them, but maybe it'd also be worth it?
Post Reply