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