Sign in to follow this  
Followers 0
Tjalve

Help understanding TCP_Send och TCP_Recv

9 posts in this topic

Hello everyone. I have been coding a bit in Autoit for the past year and i think that ive gotten faily good at it. However i have never read anything about Programming or done any serius programming befor this. So everying that i know is trail and error on my part.

Now. I want to try to create a small server/agent program that can help me inventory a small number of computers. I know that that sort of program already exists but i want to challenge myself and try to do it myself. In the end i want to be able to have a server with a small console witch tell me witch computers are on, and then be able to send "commands" to the agents.

I did a bit of coding yesterday and i used Wayfarer EXELENT server example ('?do=embed' frameborder='0' data-embedContent>>) and i think i understand most of the code. But there is still one thing that eludes me. And its TCP_Send and TCP_Recv.

When you use TCP_Send, yopu send a string (or a set of binary data) to a specific IP usinga  specific port. That mutch i understand. But what i dont understand is how TCP_Recv grabs this information. I mean, if I call TCP_Recv after I ve send the data on the other end, i ghet nothing. But if i use an infinite loop and call TCP_Recv once every 5-10ms then i get the data on the other end. So my question is, how often do i need to listen? And what heppends if the servicer just happen "not to listen" just when the client sends the data. Isnt there a chance that i might loose some data in transit? Or is there some kind of buffer that can be read?

I know that all this is working Wayfarers example, but i want to be able to write the code myself and i want to understand how this works. Anyone willing te give me a quick explanation?

Share this post


Link to post
Share on other sites



here is an example I hope will helps you

Server

Opt("GUIONEVENTMODE", 1)
GUICreate("Form1", 475, 304)
GUISetOnEvent(-3, "_EXITdb")
Global $Edit1 = GUICtrlCreateEdit("", 8, 8, 329, 241)
Global $Input1 = GUICtrlCreateInput("", 24, 264, 180, 21)
GUICtrlCreateButton("Send", 208, 264, 47, 25)
GUICtrlSetOnEvent(-1, "_Senddata")

GUICtrlCreateLabel("Select socket ", 360, 8, 70, 17)
Global $List1 = GUICtrlCreateListView("Socket list", 352, 32, 113, 253)

GUISetState(@SW_SHOW)

TCPStartup()

_Consolwrite("-> listener started on port : " & 403 & " <-")
Global $TCPL = TCPListen(@IPAddress1, 403)
If @error Then Exit

Global $arrayclien[1][3]
Global $Packmsg = "[Msgpack]"
Global $Packend = "[Endpack]"

Func _TCPAccept()
    Local $TCPA = TCPAccept($TCPL)
    If $TCPA = -1 Then Return
    Do
        $res = TCPRecv($TCPA, 50)
    Until $res <> ""
    _Sptpack($res, $TCPA)
    ReDim $arrayclien[$arrayclien[0][0] + 2][3]
    $arrayclien[0][0] += 1
    $arrayclien[$arrayclien[0][0]][0] = $TCPA
    $arrayclien[$arrayclien[0][0]][1] = GUICtrlCreateListViewItem($TCPA, $List1)
    _Consolwrite("-> New connection socket : " & $TCPA & " <-")
EndFunc   ;==>_TCPAccept

Local $Resv, $TCPR, $TCPRtmp
While 1
    Sleep(10);;
    _TCPAccept()
    For $oi = 1 To $arrayclien[0][0]
        If $oi > $arrayclien[0][0] Then ExitLoop ;;;
        $Resv = _TCPRecv($arrayclien[$oi][0], 20)
    Next
WEnd

Func _Consolwrite($txt)
    GUICtrlSetData($Edit1, GUICtrlRead($Edit1) & $txt & @CRLF)
EndFunc   ;==>_Consolwrite
Func _TCPRecv($Soker, $Len)
    $TCPR = TCPRecv($Soker, $Len)
    If @error Then
        _Consolwrite("-> Client has been disconnected : " & $Soker & " <-")
        _Dellclien($Soker)
    EndIf
    
    $TCPRtmp = $TCPR
    If $TCPR = "" Then Return $TCPRtmp

    Do
        $TCPR = TCPRecv($Soker, $Len)
        If $TCPR = "" Then ExitLoop
        $TCPRtmp &= $TCPR
    Until $TCPR = ""

    _Sptpack($TCPRtmp, $Soker)
EndFunc   ;==>_TCPRecv
Func _Sptpack($mPack, $Soker)
    Local $Splitpack = StringSplit($mPack, $Packend, 1)
    If @error Then Return
    For $ii = 0 To $Splitpack[0]
        _Getpacktyp($Splitpack[$ii], $Soker)
    Next
EndFunc   ;==>_Sptpack
Func _Getpacktyp($mPack, $Msoket)
    Select
        Case StringInStr($mPack, $Packmsg) > 0
            _Consolwrite($Msoket & " Say : " & StringTrimLeft($mPack, StringInStr($mPack, $Packmsg) + StringLen($Packmsg) - 1))
            ; Case StringInStr($mPack, $PackSendifle) > 0
            
    EndSelect
EndFunc   ;==>_Getpacktyp
Func _TCPSend($Soker, $mdata)
    TCPSend($Soker, $mdata)
EndFunc   ;==>_TCPSend
Func _Senddata()
    If GUICtrlRead(GUICtrlRead($List1)) = "" Then
        _Consolwrite("-> Please select a socket first <-")
        Return
    EndIf
    If GUICtrlRead($Input1) = "" Then Return
    _TCPSend(GUICtrlRead(GUICtrlRead($List1)), $Packmsg & GUICtrlRead($Input1) & $Packend)
EndFunc   ;==>_Senddata
Func _Dellclien($Soket)
    Dim $Arryctmp[$arrayclien[0][0]][3], $i
    For $oi = 1 To $arrayclien[0][0]
        If $Soket = $arrayclien[$oi][0] Then
            GUICtrlDelete($arrayclien[$oi][1])
            ContinueLoop
        EndIf
        $i += 1
        For $m = 0 To 2
            $Arryctmp[$i][$m] = $arrayclien[$oi][$m]
        Next
    Next
    $Arryctmp[0][0] = $arrayclien[0][0] - 1
    $arrayclien = $Arryctmp
EndFunc   ;==>_Dellclien
Func _EXITdb()
    TCPShutdown()
    Exit
EndFunc   ;==>_EXITdb

Client

Opt("GUIONEVENTMODE", 1)
GUICreate("Form1", 345, 304)
GUISetOnEvent(-3, "_EXITdb")
Global $Edit1 = GUICtrlCreateEdit("", 8, 8, 329, 241)
Global $Input1 = GUICtrlCreateInput("", 24, 264, 225, 21)
GUICtrlCreateButton("Send", 268, 264, 47, 25)
GUICtrlSetOnEvent(-1, "_Senddata")
GUISetState(@SW_SHOW)
TCPStartup()

Global $TCPL

Global $Packmsg = "[Msgpack]"
Global $Packend = "[Endpack]"

_TCPConnect()

Func _TCPConnect()
    _Consolwrite("-> connection was started wait for response <-")
    While 1
        $TCPL = TCPConnect(@IPAddress1, 403)
        If $TCPL <> -1 Then ExitLoop
    WEnd
    _Consolwrite("-> A connection was successfully established with the server <-")
    _TCPSend($TCPL, $Packmsg & "Hallo :)" & $Packend)
EndFunc   ;==>_TCPConnect

Local $Resv, $TCPR, $TCPRtmp
While 1
    Sleep(10)
    _TCPRecv($TCPL, 20)
WEnd

Func _Consolwrite($txt)
    GUICtrlSetData($Edit1, GUICtrlRead($Edit1) & $txt & @CRLF)
EndFunc   ;==>_Consolwrite
Func _TCPRecv($Soker, $Len)
    $TCPR = TCPRecv($Soker, $Len)
    If @error Then
        _Consolwrite("-> Server has been shut down <-")
        _TCPConnect()
    EndIf

    $TCPRtmp = $TCPR
    If $TCPR = "" Then Return $TCPRtmp

    Do
        $TCPR = TCPRecv($Soker, $Len)
        If $TCPR = "" Then ExitLoop
        $TCPRtmp &= $TCPR
    Until $TCPR = ""
    
    _Sptpack($TCPRtmp)
EndFunc   ;==>_TCPRecv
Func _Sptpack($mPack)
    Local $Splitpack = StringSplit($mPack, $Packend, 1)
    If @error Then Return
    For $ii = 0 To $Splitpack[0]
        _Getpacktyp($Splitpack[$ii])
    Next
EndFunc   ;==>_Sptpack
Func _Getpacktyp($mPack)
    Select
        Case StringInStr($mPack, $Packmsg) > 0
            _Consolwrite("Server Say : " & StringTrimLeft($mPack, StringInStr($mPack, $Packmsg) + StringLen($Packmsg) - 1))
            ;Case StringInStr($mPack, $PackSendifle) > 0
    EndSelect
EndFunc   ;==>_Getpacktyp
Func _TCPSend($Soker, $mdata)
    TCPSend($Soker, $mdata)
EndFunc   ;==>_TCPSend
Func _Senddata()
    If GUICtrlRead($Input1) = "" Then Return
    _TCPSend($TCPL, $Packmsg & GUICtrlRead($Input1) & $Packend)
EndFunc   ;==>_Senddata
Func _EXITdb()
    TCPShutdown()
    Exit
EndFunc   ;==>_EXITdb

Share this post


Link to post
Share on other sites

I thank you. But i already have working examples. Burt i want to understand how it works. Why do you need to have TCP_recv in a loop to be able to get the data?

Share this post


Link to post
Share on other sites

I thank you. But i already have working examples. Burt i want to understand how it works. Why do you need to have TCP_recv in a loop to be able to get the data?

Because you won't receive 400 or 50 or 1 MB in 1ms :)


Heroes, there is no such thing

One day I'll discover what IE.au3 has of special for so many users using it.
C'mon there's InetRead and WinHTTP, way better
happy.png

Share this post


Link to post
Share on other sites

Because you won't receive 400 or 50 or 1 MB in 1ms :)

Of course i know that.  I just wish someone could tell me how it works.

Share this post


Link to post
Share on other sites

Maybe, you should explain what it is you're having difficulties understanding, rather than just throwing up your hands and saying "I don't get it".


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

For $i = 1 To 200
    $sRecv = TCPRecv($nSocket, 1024); <- Maximum bytes to receive.
    If @error Then
        ExitLoop
    ElseIf $sRecv <> '' Then
        $sBuffer &= $sRecv
        If StringRight($sRecv, 4) = @CRLF & @CRLF Then; <- EOF (End Of File)
            ExitLoop
        EndIf
        $i = 0; <- reset timeout
    EndIf
    Sleep(10)
Next

; Do something with $sBuffer at this point.

In the example above, TCPRecv() will receive data at a rate up to 1024 bytes per call.

If you sent a file that was 10KB's in size, it would take at least 10 turns of the

loop to receive all the data.

You might think you could increase the maximum bytes to 10240 to receive the file

in one call, but it just doesn't work that way.

In reality, the behavior behind the scenes, goes something like this:

Loop#

1 = blank

2 = 1024 bytes received

3 = blank

4 = 512 bytes received

5 = 1024 bytes received

6 = 256 bytes received

7 = blank

8 = 1024 bytes received

9 = etc, etc.

So this is the reason you need a loop to receive data.

You should always use some type of escape method when using loops

of this nature, so that your script won't get stuck there when

something goes wrong.

I personally use a For loop as a timeout gimmick (see example).

For more information than you really want to know, see MSDN:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms740121(v=vs.85).aspx

Edited by ripdad

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

Share this post


Link to post
Share on other sites

For $i = 1 To 200
    $sRecv = TCPRecv($nSocket, 1024); <- Maximum bytes to receive.
    If @error Then
        ExitLoop
    ElseIf $sRecv <> '' Then
        $sBuffer &= $sRecv
        If StringRight($sRecv, 4) = @CRLF & @CRLF Then; <- EOF (End Of File)
            ExitLoop
        EndIf
        $i = 0; <- reset timeout
    EndIf
    Sleep(10)
Next

; Do something with $sBuffer at this point.
In the example above, TCPRecv() will receive data at a rate up to 1024 bytes per call.

If you sent a file that was 10KB's in size, it would take at least 10 turns of the

loop to receive all the data.

You might think you could increase the maximum bytes to 10240 to receive the file

in one call, but it just doesn't work that way.

In reality, the behavior behind the scenes, goes something like this:

Loop#

1 = blank

2 = 1024 bytes received

3 = blank

4 = 512 bytes received

5 = 1024 bytes received

6 = 256 bytes received

7 = blank

8 = 1024 bytes received

9 = etc, etc.

So this is the reason you need a loop to receive data.

You should always use some type of escape method when using loops

of this nature, so that your script won't get stuck there when

something goes wrong.

I personally use a For loop as a timeout gimmick (see example).

For more information than you really want to know, see MSDN:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms740121(v=vs.85).aspx

 

That was EXACTLY what i was looking for. Thank you ripdad.

I will read trough the MSDN article.

So basicly if i use TCP_Send to send 1MB of data, i will need to use TCPRecv at least 10 times to get the whole data. So if there are several clients sending data at the same time, how do you know what data goes where (since it gets chopped up). Or if the same client first send one file of 800KB and then another file with 2KB and then another one at 3MB. How do you know when the file "start" and "end"? I guess i need to create some kind of buffer to be able to handle bigger data.

One other question. Is there any best practises regarding the size? Is 1024 a good number or is 2048 better? And why would one be better then the other?

Thank you again.

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

So basicly if i use TCP_Send to send 1MB of data, i will need to use TCPRecv at least 10 times to get the whole data.

Yep, If you set max bytes (or max length) to 102400.

At 10240, 1MB of data would take a minimum of 100 turns.

So if there are several clients sending data at the same time, how do you know what data goes where (since it gets chopped up). Or if the same client first send one file of 800KB and then another file with 2KB and then another one at 3MB. How do you know when the file "start" and "end"? I guess i need to create some kind of buffer to be able to handle bigger data.

There are many scripts in the examples section of the forum, that show you

how to deal with multiple clients and the incoming data. I hope you are up

to speed on using arrays. If not, then that would be a good place to start!

One other question. Is there any best practises regarding the size?

Is 1024 a good number or is 2048 better? And why would one be better

then the other?

Seems everyone has an opinion on this - but the research says the majority use 8192.

Mainly because most website servers send data in 8192 byte chunks.

Thank you again.

Glad I could help.

Edited by ripdad

"The mediocre teacher tells. The Good teacher explains. The superior teacher demonstrates. The great teacher inspires." -William Arthur Ward

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