Sign in to follow this  
Followers 0
Chimp

has the TCPRecv() command finished to receiving data ?

12 posts in this topic

hello,

If i use TCPRecv($Socket, 2048) but TCPSend() transmit more than 2048 bytes, i lose data.

Is there a way to know when the TCPRecv() command has no more data to read from a single TCPSend()?

for example, I wonder if there's a way similar to that of reading the output of a dos command ?

; read all the output of a dos command
$iPID = Run('"' & @ComSpec & '" /c ' & $sCommand, "", @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
While 1
$sOutput &= StdoutRead($iPID, False, False)
If @error Then ; <--- no more data to read <---
ExitLoop
EndIf
Sleep(10)
WEnd

thanks


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites



thanks JohnOne

my problem is that if I receive a "message" longer than 2048, I lose the final part of the "message"

(of course I do not want to increase the number 2048 in the command TCPRecv ())

what I try to do is a server that receives dos commands from one or more clients, executes them and returns the result to the client.

inserting a DOS command in the upper section of the GUI and clicking on the button, you will receive the result of the command that is executed by the server, and displayed in the lower box.

if I enter dos commands that produce a shorter output (eg ipconfig), there are no problems, but if I enter a command with a long output (eg dir c:\ /s) then I will lose part of the output.

the server:

; this server script will receive and execute dos commands from a remote client
; and return the result to the caller client
#include <Array.au3>
#include <Constants.au3>

TCPStartup() ; Init TCP/IP

Dim $Socket_Data[1] ; will hold track of incomming clients

$Socket_Data[0] = 0 ; start with 0 clients connected

$Listen = TCPListen(@IPAddress1, 9057, 5)

If @error Then
ConsoleWrite('!--> TCPListen error number ( ' & @error & ' ).' & @CRLF)
TCPShutdown()
Exit ; --> End due to an error.
EndIf

While 1
For $x = $Socket_Data[0] To 1 Step -1 ; scans all connected clients

$Recv = TCPRecv($Socket_Data[$x], 2048) ; check if there is data to read

If $Recv Then ; If we received something, the $Recv variable will hold that data.

Switch $Recv ; analyze incoming messages
Case "kill server" ; stop all connections and exit
for $x0 = $Socket_Data[0] To 1 Step -1
TCPSend($Socket_Data[$x0], "Server killed" & @CRLF) ; notify to client
Sleep(2000)
TCPRecv($Socket_Data[$x0], 512)
TCPCloseSocket($Socket_Data[$x0]) ; disconnect client
Next
TCPShutdown()
Exit ; the end
Case "bye" ; disconnect only this client
TCPSend($Socket_Data[$x], "bye bye client " & _SocketToIP($Socket_Data[$x]))
TCPCloseSocket($Socket_Data[$x])
_ArrayDelete($Socket_Data, $x) ; delete client from array
$Socket_Data[0] -= 1
Case Else ; run the dos command and returns the result to the client that requested it

TCPSend($Socket_Data[$x],_GetDOSOutput($Recv))

EndSwitch
EndIf
Next
_Accept() ; _Accept will check for new connections so we will call it once on every loop.
WEnd
; ==========================
; Functions zone
; ==========================
Func _Accept()
Local $Accept = TCPAccept($Listen) ; are there new connection requests ?
If $Accept <> -1 Then
_ArrayAdd($Socket_Data, $Accept)
$Socket_Data[0] += 1
EndIf
EndFunc ;==>_Accept

Func _GetDOSOutput($sCommand) ; run the dos command and returns the result
Local $iPID, $sOutput = ""
$iPID = Run('"' & @ComSpec & '" /c ' & $sCommand, "", @SW_HIDE, $STDERR_MERGED) ; $STDERR_CHILD + $STDOUT_CHILD)
While 1
$sOutput &= StdoutRead($iPID, False, False)
If @error Then
ExitLoop
EndIf
Sleep(10)
WEnd
Return $sOutput
EndFunc ;==>_GetDOSOutput

; Function to return IP Address from a connected socket.
;----------------------------------------------------------------------
Func _SocketToIP($SHOCKET)
Local $sockaddr, $aRet

$sockaddr = DllStructCreate("short;ushort;uint;char[8]")

$aRet = DllCall("Ws2_32.dll", "int", "getpeername", "int", $SHOCKET, _
"ptr", DllStructGetPtr($sockaddr), "int*", DllStructGetSize($sockaddr))
If Not @error And $aRet[0] = 0 Then
$aRet = DllCall("Ws2_32.dll", "str", "inet_ntoa", "int", DllStructGetData($sockaddr, 3))
If Not @error Then $aRet = $aRet[0]
Else
$aRet = 0
EndIf

$sockaddr = 0

Return $aRet
EndFunc ;==>_SocketToIP

the client

#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#Region ### START Koda GUI section ### Form=
$Form1 = GUICreate("Form1", 623, 323, 280, 165)
$Edit1 = GUICtrlCreateEdit("", 8, 8, 593, 113)
GUICtrlSetData(-1, "Edit1")
$Edit2 = GUICtrlCreateEdit("", 8, 184, 593, 129)
GUICtrlSetData(-1, "Edit2")
$Button1 = GUICtrlCreateButton("Button1", 8, 128, 593, 17)
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###

TCPStartup()

Local $g_IP = @IPAddress1 ; same as server this example

While 1
$nMsg = GUIGetMsg()
Switch $nMsg
Case $GUI_EVENT_CLOSE
Exit
Case $Button1

$Socket = TCPConnect($g_IP, 9057) ; call the server

If @error Then
ConsoleWrite('!--> TCPConnect error number ( ' & @error & ' ).' & @CRLF)
Exit
EndIf

; ConsoleWrite(GUICtrlRead($Edit1) & @CRLF); echo on console

TCPSend($Socket, GUICtrlRead($Edit1)) ; send the content of upper Edit1

Do ; wait for answer from server

$Recv = TCPRecv($Socket, 2048) ; <- HERE LOST OF DATA <---
; ConsoleWrite(".")
Until $Recv

GUICtrlSetData($Edit2,$Recv) ; write the result from server to Edit2

; ConsoleWrite(@CRLF & $Recv & @CRLF) ; echo on console
EndSwitch
WEnd

P.S.

I wrote these scripts by modifying the examples in this tutorial:

thanks


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites

you mean this part of the help file?;

;
While 1
$msg = GUIGetMsg()

; GUI Closed
;--------------------
If $msg = $GUI_EVENT_CLOSE Then ExitLoop

; Try to receive (up to) 2048 bytes
;----------------------------------------------------------------
$recv = TCPRecv($ConnectedSocket, 2048)

; If the receive failed with @error then the socket has disconnected
;----------------------------------------------------------------
If @error Then ExitLoop

; convert from UTF-8 to AutoIt native UTF-16
$recv = BinaryToString($recv, 4)

; Update the edit control with what we have received
;----------------------------------------------------------------
If $recv <> "" Then GUICtrlSetData($edit, _
$szIP_Accepted & " > " & $recv & @CRLF & GUICtrlRead($edit))
WEnd

I think this @ error occurs only if the client disconnects, or am I wrong?


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites

I use a large receive buffer size (32000) so that I know I will receive everything that's being sent. I believe data will sit in the receive buffer until you call TCPRecv. If there's more data in the buffer than your TCPRecv is requesting, then it will leave the excess data in the buffer until the next time you call TCPRecv. I guess you could do a While loop until TCPRecv returns nothing every time and just append the received data to a string.

Share this post


Link to post
Share on other sites

hello sentry07, thanks for your suggestion

maybe, like you say, read the data with TCPRecv () until it receives a null string can fit.

I do not know if it's the best way, but this way it works.

(...who knows if a delay on the network could result in an empty string even if there is data to be received?...)

this is the reading loop that works.

TCPSend($Socket, GUICtrlRead($Edit1)) ; send the request to server

Do ; wait for answer from server
     $RecvBuffer = TCPRecv($Socket, 2048) ; first part of receiving data goes in $RecvBuffer
     sleep(100)
Until $RecvBuffer ; stay here if nothing received yet

While 1 ; read more dada if available
     $Recv = TCPRecv($Socket, 2048) ; is there more data ?

     if $Recv = "" then ; <--- if no more data to read then exit
ExitLoop
     EndIf

     $RecvBuffer = $RecvBuffer & $Recv ; append new data to buffer

     Sleep(10)
WEnd

$Recv = $RecvBuffer ; complete output is in $Recv

Bye and thanks again


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites

(...who knows if a delay on the network could result in an empty string even if there is data to be received?...)

Yes network timeout can result in receiving nothing. Sometimes you have to set the recv timeout of the connection.


Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

i did name it Third Part: Understanding for how is msg received on server

but that don't mean you can't use it on client

You need to know where msg starts and where msg ends, for example if server sends data but before it started sending data it put '#SS#'&and after he stop sending data he sends '*ES*' or something similar of your choice then you only need to check on client with split-string-betwean or something third. For example if stringinstr *ES* so that you know you recived some msg to the end and current data is ready to rejoin rest of msg with old data.

Or you can check if string between returns array and you tell it then to split your string because you know you received msg from start to end and filter out msg that isn't complete so that you rejoin it next time on tcp receive

dirty example:

#include <String.au3>
#include <Array.au3>
;small test
;~ $tcpstr = "#SS#Hi Im Stri"
;larger test
$tcpstr = "#SS#Hi Im String1*ES*#SS#Hi Im String2*ES*#SS#Hi Im String3*ES*#SS#Hi Im String4*ES*#SS#Hi Im String5*ES*#SS#Hi Im Stri"
If $tcpstr Then
    Local $aArray1 = _StringBetween($tcpstr, '#SS#', '*ES*')
    If IsArray($aArray1) Then
        Local $lenght = UBound($aArray1) - 1
        For $x = 0 To $lenght
            $tcpstr = StringReplace($tcpstr, '#SS#' & $aArray1[$x] & '*ES*', '')
        Next
        _ArrayDisplay($aArray1, 'Recived nmbr: '&$lenght+1)
        If $tcpstr Then
            MsgBox(0, "Msg nmbr :"&$lenght+2&". Still w8ing  to recive end on this msg", $tcpstr)
            ;rejoin it
        Else
            MsgBox(0, "Empty", 'no more msgs at the moment')
        EndIf
    Else
        MsgBox(0, "Still w8ing  to recive end on this msg", $tcpstr)
        ;rejoin it
    EndIf
Else
    MsgBox(0, "Empty", 'no more msgs at the moment')
EndIf
Edited by bogQ

TCP server and client - Learning about TCP servers and clients connection
Au3 oIrrlicht - Irrlicht project
Au3impact - Another 3D DLL game engine for autoit. (3impact 3Drad related)



460px-Thief-4-temp-banner.jpg
There are those that believe that the perfect heist lies in the preparation.
Some say that it’s all in the timing, seizing the right opportunity. Others even say it’s the ability to leave no trace behind, be a ghost.

 

Share this post


Link to post
Share on other sites

hello bogQ,

thanks for your answer

I understood the mechanism of precede with '# SS #' at the beginning and append '** ES **' at the end of each message,

in this way I know exactly where it begins and where it ends any message,it is also possible to send multiple messages in a single TCPSend()

I thought that TCPSend () would automatically add an EOF or EOT ascii character or something similar for each transmission, but evidently it is not so.

I have to manage on my own traffic data.

please, another question:

when I do this close sequence on the server:

TCPCloseSocket($Socket)

TCPShutdown()

Exit

and then I control the active tasks on the computer I see that the myserver.exe task is still alive till I close also the client (after the close sequence myserver.exe it remain in the task list also if it do not give no more answers to client's call), in fact only when i close the client also the server disappear from the task list.

is there a better way to shut down the server?

thanks again

P.S.

I found your tutorials on tcpip very useful, clear and easy to follow.

my compliments, nice work!


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

I have some questions

are you shure you don't have something else in there that don't allow you to exit? did you try to put msgbox or consolewrite to see where it hangs or Opt("TrayIconDebug", 1)? Maybe some opened program or file with run command that strangely hangs autoit program.

why do you sleep 2 sec after every TCPSend on server? if its to ensure that msg is received about server dccing there isn't any need, error check on client shud inform client that connection with the server is reseted. and why do you try to receive "TCPRecv($Socket_Data[$x0], 512)?" after that sleep if your already trying to disconnect client?

I did try your script under win7 with 'kill server' uncompiled and compiled, it worked with no problem, server exited while client still stayed opened, and server did not hang in task manager.

Edited by bogQ

TCP server and client - Learning about TCP servers and clients connection
Au3 oIrrlicht - Irrlicht project
Au3impact - Another 3D DLL game engine for autoit. (3impact 3Drad related)



460px-Thief-4-temp-banner.jpg
There are those that believe that the perfect heist lies in the preparation.
Some say that it’s all in the timing, seizing the right opportunity. Others even say it’s the ability to leave no trace behind, be a ghost.

 

Share this post


Link to post
Share on other sites

I have to make more test on the server shutdown because sometime it stop correctly sometime with a delay and sometime only after I turn off the client.

... I also compiled the server for x86 but I use it both on win xp x32 than on win7 x64, perhaps this fact can affect ....

regarding the delays of 2 seconds and the TCPRecv ($ SOCKET_DATA [$ x0], 512), well,

I do not know why they are used, :blink:

really I asked the same question to myself when i've "blindly" :idiot: ripped and adapted the closing sequence from the example script in the autoit help for the command TCPCloseSocket()

this is the function from the example of the AutoIt Help for the command TCPCloseSocket :

Func Cleanup()
;ON SCRIPT EXIT close opened sockets and shutdown TCP service
;----------------------------------------------------------------------
If $ConnectedSocket > -1 Then
TCPSend($ConnectedSocket, "~~bye")
Sleep(2000)
TCPRecv($ConnectedSocket, 512)
TCPCloseSocket($ConnectedSocket)
EndIf
TCPCloseSocket($MainSocket)
TCPShutdown()
EndFunc ;==>Cleanup

bye


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0