Network data server/client SendNetworkString/ReceiveNetworkData

Just starting out? Need help? Post your questions and find answers here.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by Oso »

HeX0R wrote: Tue Jan 10, 2023 2:02 pm Sending data via network needs a little more things to consider, that's why I said above: Most people are handling SendNetworkData/String wrong. For a result of -1 you need to know, what the reason is, the answer here from idle might be a good start: viewtopic.php?t=42843 That could be WSAEWOULDBLOCK... then simply wait and send it again, but it might be also a connection loss then it wouldn't help to resend it. Therefore, unfortunately, for a robust application you need the help of the API to find out.
Many thanks indeed for these comments HeX0R. I have just implemented @Idle's code linked above. It worked well, so long as I increased the 5-second timeout because my data transmission suffers from severe bottleneck delays. I also converted it to SendNetworkString(). So my thanks to you also Idle for that 2010 code :)

This is what I found : a client-side buffer size of 16,384 bytes showed a bottleneck of ~60 seconds, during receipt of data. It was interesting — of the 35 MB data stream, it paused at around 10% and then sent all the remainer without stopping again. Increasing the client-side to 65535, 35 MB completed in 6 seconds. My feeling, regardless, is that it benefits by adding a sending delay as I think it seems possible to overload the sending of data.
In general you should use integers for objects created with #PB_Any, yes. But besides that, longs (or word or bytes) should be only used, where you are forced to. E.g. for API calls, or inside structures. For anything else, no matter how big the content will be: Use integers!
Thanks for this advice. A job for tomorrow :D
User avatar
idle
Always Here
Always Here
Posts: 5039
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by idle »

You also need to use the same on the recive end, tcp stream control is a bit of a mystery black box. If you get wsawouldblock you just need to add a delay and try again.
I think Tcp by default uses slow start, it fills up its send buffers before streaming and that's where it will ask you to back off. Think of it as a ring buffer getting full or empty. You can over ride the slow start if it's causing issues

edit:
here I found an old code, it still works the examples a bit odd, though I was as your are sleep deprived from coding.
client sends random sized chunks 1/2 mb
viewtopic.php?t=34493
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by Oso »

idle wrote: Tue Jan 10, 2023 9:49 pm You also need to use the same on the recive end, tcp stream control is a bit of a mystery black box. If you get wsawouldblock you just need to add a delay and try again.
Thanks Idle, yes I've thought about the receiving end. Do you mean the aspect of the receiving end that sends back command data to the server, or the just the receiving of data from the server? :D For the receiving side, it only displays what it receives, so if it only receives 10 bytes of a total 1,000 it doesn't matter — it just prints those ten bytes and waits for more. It does also send very short commands back to the server. They aren't really a priority, but I'll do that also.
idle wrote: Tue Jan 10, 2023 9:49 pm I think Tcp by default uses slow start, it fills up its send buffers before streaming and that's where it will ask you to back off. Think of it as a ring buffer getting full or empty. You can over ride the slow start if it's causing issues
That would explain it, yes. Since I'm displaying the data as it's received, I can gauge quite clearly how the speed is going. It definitely looks very slow at the start, as you say. It waits for a long time and then speeds up remarkably fast, such that the remainder takes only a second or two. After I increased the buffer to 64kb, that delay was eliminated.

Thanks for the code, I'll look tomorrow, yes sleep is a bit limited at the moment :( All the best.
User avatar
idle
Always Here
Always Here
Posts: 5039
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by idle »

Oso wrote: Tue Jan 10, 2023 10:46 pm Thanks for the code, I'll look tomorrow, yes sleep is a bit limited at the moment :( All the best.
well at least your having fun and being well supported.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by Oso »

Thanks to all for the input on this subject. #WSAEWOULDBLOCK has enabled me to send network communication to connecting clients 100% reliably. In principle, I used the example from the 2010 post viewtopic.php?p=328229#p328229 (code snippet below).

A question I have — although this appears to work perfectly while stress-testing with 33 MB of data sent simultaneously to multiple connecting clients through a thread for each client, how does the call err = WSAGetLastError_() know that I'm only interested in this particular client connection?

Code: Select all

  Repeat
    result.i = SendNetworkString(clientid.i, senddata.s, #PB_Ascii)
    If result.i <> -1
      transmit.i + result.i                                             ; Total of total bytes sent
      Delaytime = 0                                                     ; Remove delay timer
      timeout = ElapsedMilliseconds() + #NETSENDTIMO                    ; Reset next timeout
    Else

      Apierr.i = WSAGetLastError_()                                     ; <<========   Get network API error
      Select Apierr.i
        Case #WSAECONNABORTED                                           ; Aborted connection, not attempt to send further
          ConnectionDropped.b = #True                                   ; Set flag to indicate connection was dropped
          PrintN("Connection dropped")
          Break                                                         ; Break out of loop
        Case #WSAEWOULDBLOCK                                            ; Attempt to send to non-blocking, need to wait
          Delaytime = 100                                               ; Set delay to wait for network operation
          PrintN("Delaying 100")
      EndSelect
User avatar
HeX0R
Addict
Addict
Posts: 980
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by HeX0R »

WSAGetLastError
The return value indicates the error code for this thread's last Windows Sockets operation that failed.
And:
The WSAGetLastError function returns the last error that occurred for the calling thread.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by Oso »

HeX0R wrote: Thu Jan 12, 2023 7:09 pm
The return value indicates the error code for this thread's last Windows Sockets operation that failed.
And:
The WSAGetLastError function returns the last error that occurred for the calling thread.
Thank you HeX0R that's very helpful, I appreciate you sending that.
User avatar
HeX0R
Addict
Addict
Posts: 980
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by HeX0R »

There is an almost 17 year old Feature Request for a NetworkError() implementation.
I don't know why this gets always ignored, from my point of view it is a must for the network library (cross-platform of course).
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by Oso »

HeX0R wrote: Thu Jan 12, 2023 8:27 pm There is an almost 17 year old Feature Request for a NetworkError() implementation. I don't know why this gets always ignored, from my point of view it is a must for the network library (cross-platform of course).
I nearly mentioned in my last message that at some point in the future, I'd like to be able to say that my software will include a Linux port, but I didn't want to digress from the subject of the thread. I haven't any idea of the equivalent APIs such as this one though.

I'm a bit embarrassed because, coming from a legacy database environment, I've never needed to understand a Windows API. I'm still quite uncomfortable with them. In fact, had I managed to find that document that you provided, I doubt that I would have been convinced on my own, that "calling thread" necessarily equates with PureBasic threads.

But yes, I think it's important for some functions to replace the need for APIs, at least where they are mainstream requirements, which I think this is.
User avatar
idle
Always Here
Always Here
Posts: 5039
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by idle »

the socket error are the same cross platform just need to import errno on linux and osx
viewtopic.php?p=593536#p593536

Code: Select all


;ImportC "" 
;   errno_ ()  
;EndImport 

;*WSA Constants

#WSA_INVALID_HANDLE = 6
#WSA_NOT_ENOUGH_MEMORY = 8
#WSA_INVALID_PARAMETER = 87
#WSA_OPERATION_ABORTED = 995
#WSA_IO_INCOMPLETE = 996
#WSA_IO_PENDING = 997
#WSAEINTR = 10004
#WSAEBADF = 10009
#WSAEACCES = 10013
#WSAEFAULT = 10014
#WSAEINVAL = 10022
#WSAEMFILE = 10024
#WSAEWOULDBLOCK = 10035
#WSAEINPROGRESS = 10036
#WSAEALREADY = 10037
#WSAENOTSOCK = 10038
#WSAEDESTADDRREQ = 10039
#WSAEMSGSIZE = 10040
#WSAEPROTOTYPE = 10041
#WSAENOPROTOOPT = 10042
#WSAEPROTONOSUPPORT = 10043
#WSAESOCKTNOSUPPORT = 10044
#WSAEOPNOTSUPP = 10045
#WSAEPFNOSUPPORT = 10046
#WSAEAFNOSUPPORT = 10047
#WSAEADDRINUSE = 10048
#WSAEADDRNOTAVAIL = 10049
#WSAENETDOWN = 10050
#WSAENETUNREACH = 10051
#WSAENETRESET = 10052
#WSAECONNABORTED = 10053
#WSAECONNRESET = 10054
#WSAENOBUFS = 10055
#WSAEISCONN = 10056
#WSAENOTCONN = 10057
#WSAESHUTDOWN = 10058
#WSAETOOMANYREFS = 10059
#WSAETIMEDOUT = 10060
#WSAECONNREFUSED = 10061
#WSAELOOP = 10062
#WSAENAMETOOLONG = 10063
#WSAEHOSTDOWN = 10064
#WSAEHOSTUNREACH = 10065
#WSAENOTEMPTY = 10066
#WSAEPROCLIM = 10067
#WSAEUSERS = 10068
#WSAEDQUOT = 10069
#WSAESTALE = 10070
#WSAEREMOTE =10071
#WSASYSNOTREADY = 10091
#WSAVERNOTSUPPORTED = 10092
#WSANOTINITIALISED = 10093
#WSAEDISCON = 10101
#WSAENOMORE = 10102
#WSAECANCELLED = 10103
#WSAEINVALIDPROCTABLE = 10104
#WSAEINVALIDPROVIDER = 10105
#WSAEPROVIDERFAILEDINIT = 10106
#WSASYSCALLFAILURE = 10107
#WSASERVICE_NOT_FOUND = 10108
#WSATYPE_NOT_FOUND = 10109
#WSA_E_NO_MORE = 10110
#WSA_E_CANCELLED = 10111
#WSAEREFUSED = 10112
#WSAHOST_NOT_FOUND = 11001
#WSATRY_AGAIN = 11002
#WSANO_RECOVERY = 11003
#WSANO_DATA = 11004
#WSA_QOS_RECEIVERS = 11005
#WSA_QOS_SENDERS = 11006
#WSA_QOS_NO_SENDERS = 11007
#WSA_QOS_NO_RECEIVERS = 11008
#WSA_QOS_REQUEST_CONFIRMED = 11009
#WSA_QOS_ADMISSION_FAILURE = 11010
#WSA_QOS_POLICY_FAILURE = 11011
#WSA_QOS_BAD_STYLE = 11012
#WSA_QOS_BAD_OBJECT = 11013
#WSA_QOS_TRAFFIC_CTRL_ERROR = 11014
#WSA_QOS_GENERIC_ERROR = 11015
#WSA_QOS_ESERVICETYPE = 11016
#WSA_QOS_EFLOWSPEC = 11017
#WSA_QOS_EPROVSPECBUF = 11018
#WSA_QOS_EFILTERSTYLE = 11019
#WSA_QOS_EFILTERTYPE = 11020
#WSA_QOS_EFILTERCOUNT = 11021
#WSA_QOS_EOBJLENGTH = 11022
#WSA_QOS_EFLOWCOUNT = 11023
#WSA_QOS_EUNKOWNPSOBJ = 11024
#WSA_QOS_EPOLICYOBJ = 11025
#WSA_QOS_EFLOWDESC = 11026
#WSA_QOS_EPSFLOWSPEC = 11027
#WSA_QOS_EPSFILTERSPEC =11028
#WSA_QOS_ESDMODEOBJ = 11029
#WSA_QOS_ESHAPERATEOBJ = 11030
#WSA_QOS_RESERVED_PETYPE = 11031
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by Oso »

idle wrote: Thu Jan 12, 2023 10:08 pm the socket error are the same cross platform just need to import errno on linux and osx
Thanks Idle, I'll cross that bridge when I come to it then :?
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by Oso »

Hello all, I thought I would post a follow-up to this, as it was an interesting discussion about SendNetworkString() and the need to deal with zero bytes sent (or fewer bytes than the total). The thing I noticed during testing, is that if the application is sending text to a network client and the client disconnects the session, then it's important to deal with this, otherwise the main process can crash. As HeX0R referred to, there's a tendency to omit this test. The example in the PB documentation also doesn't deal with this, so perhaps it isn't surprising.

My application uses threads launched from the main process, so if a client device connects and issues an instruction to the server, the server will start a new thread to send the required data back to the client. If the client disconnects, the below code which is based on Idle's code from 2010, will time-out. It was necessary for me to pass this timeout condition back to the calling procedure, to prevent it from trying to send more data. If it didn't stop sending more data, the whole server application crashed. Notably, although the sending of data was performed in a thread, if it continued to send more data, the main application process and all the other threads crashed together.

It has taken me a while to implement a method of preventing more data from being sent. It sounds easy but my application contains a lot of lines that send data. To make these changes easier, I pass the resulting error code stopflag.b back into the next output line. Therefore, I don't interrupt the thread's execution, but I just suppress it from sending output.

Code: Select all

stopflag.b = Send_Client(clientid.i, "Welcome to the server test service", 1, stopflag.b)
stopflag.b = Send_Client(clientid.i, "Here is a listing...", 1, stopflag.b)
The output code is now as follows (not called from the above, but is a sub-process).

Code: Select all

; **
; **  Send network string, handling timeout and resending, with check on bytes sent
; **  Based on https://www.purebasic.fr/english/viewtopic.php?t=42843
; **
Procedure Send_Net_String(clientid.i, senddata.s)
  
  Protected delaytime.i,          transmit.i,     Result.i,    timeout.i
  Protected sendbytes.i
  Protected ConnectionDropped.b,  btimeout.b
  Protected Apierr.i
  
  sendbytes.i = Len(senddata.s)
  Timeout = ElapsedMilliseconds() + #NETSENDTIMO
  
  Repeat
    result.i = SendNetworkString(clientid.i, senddata.s, #PB_Ascii)
    If result.i <> -1
      transmit.i + result.i                                             ; Total of total bytes sent
      Delaytime = 0                                                     ; Remove delay timer
      timeout = ElapsedMilliseconds() + #NETSENDTIMO                    ; Reset next timeout
    Else

      Apierr.i = WSAGetLastError_()                                     ; Get network API error
      Select Apierr.i
        Case #WSAECONNABORTED                                           ; Aborted connection, not attempt to send further
          ConnectionDropped.b = #True                                   ; Set flag to indicate connection was dropped
          PrintN("Send_Net_String : Connection dropped")
          Break                                                         ; Break out of loop
        Case #WSAEWOULDBLOCK                                            ; Attempt to send to non-blocking, need to wait
          Delaytime = 100                                               ; Set delay to wait for network operation
          PrintN("Send_Net_String : Delaying 100")
      EndSelect
      
      If ElapsedMilliseconds() > timeout                                ; Reached the timeout, so give up sending
        btimeout.b = #True                                              ; Set the status to return
        PrintN("Send_Net_String : Timed-out")
        Break                                                           ; Break out of the loop
      EndIf
    EndIf
    Delay(Delaytime)                                                    ; Perform the wait delay
    
  Until transmit.i >= sendbytes.i
  
  If ConnectionDropped Or bTimeout
    ProcedureReturn -1
  Else
    ProcedureReturn transmit.i
  EndIf
  
EndProcedure
tatanas
Enthusiast
Enthusiast
Posts: 198
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by tatanas »

Hi,
Is there a Linux version available of your Send_Net_String() ?

It seems we should use
poll() or select() then
getsockopt_(socketID, #SOL_SOCKET, #SO_ERROR, @option_value, SizeOf(option_value))
and check option_value but I can't make it to work.
Windows 10 Pro x64
PureBasic 6.04 x64
tatanas
Enthusiast
Enthusiast
Posts: 198
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Network data server/client SendNetworkString/ReceiveNetworkData

Post by tatanas »

Hi,

I've got the same problem with SendNetworkString() under Linux (Debian 11).
I can't post my code (too long) then I will explain.

The tcp server is running on Linux, the client on Windows.
When the server wants to send a big string, first I split this string in 65000 bytes chunks that I put in a list. Then I read this list and SendNetworkString() for each chunk. My problem is that sometimes SendNetworkString() returns less than 65000 bytes but errno return 0 (no problem). This problem appears almost each time at first server launch and works fine after.
If I add a delay(100) between each SendNetworkString(), the problem disappears.

Example :
ret=65053 ; first chunk packet
errno=0
ret=65000
errno=0
ret=65000
errno=0
ret=65000
errno=0
ret=62020 ; problem !
errno=0
ret=65000
errno=0
ret=65000
errno=0
ret=65000
errno=0
ret=30293 ; last chunk packet
I know for sure that errno is working well because it returns errors (EWOULDBLOCK (11)) very rarely.
I know I should change the sending/receiving packet processing to resend the incomplete chunk but it will be a lot of work...

Is there a way to prevent this SendNetworkString() problem apart from adding a delay ?

Thanks for your time.
Windows 10 Pro x64
PureBasic 6.04 x64
Post Reply