Jump to content

TCPRecv and v3.3.12.0


Recommended Posts

Before I start to update the standard TCP functions,

can I have a description of difference between the 2 approaches?

Thanks for the help

Could you be a little more specific? What two approaches?

Thanks.

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

Link to comment
Share on other sites

Different coding styles.

My functions are generally self-contained. His is separated in his UDF.

There's a few other tidbit's here and there.

---

We are down to the wire - perhaps 98%.

It's been raining here for several days and it gave me a chance to do some

research. I finally found what I was looking for, just a few hours ago.

You cannot imagine the dozens of ways that coders handle TCP calls.

It's just incredible. I followed a trail to a Unix group, which led

to Linux group, and which led to a TCP/IP newsgroup.

Lions and Tigers and Bears, oh my!

Anyways, I gathered this information along the way:

Security concerns of non-blocking sockets:

It's the most hassle-free and most efficient, but it does have one drawback.

Some other running program on your system has the ability to read the socket.

To me, this is not an issue -- but it's probably not a good idea in the wild.

So, I continued on to find the best way to test a socket for incoming data.

I won't bore you with the wrong ways and dead ends. Suffices to say, the

solution was found and the testing went well in blocking mode.

_TCPConnect() was reverted to return a blocking socket.

_WSA_TCPRecv now has a "ioctlsocket" call to test if data is available.

If no data, it returns 0 bytes in @extended, without making any "recv" calls.

I need to do some more testing in various situations with it.

The script will be posted, perhaps later today or tomorrow, hopefully.

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

Link to comment
Share on other sites

I will follows your suggestions.

just to be sure the uploaded AutoIt3.exe is incorporating the @progAndy non bloking socket.

I need to to the suhhested stuff for TCPRecv()

Stay tune

Link to comment
Share on other sites

I will follows your suggestions.

Be careful, no one is perfect. It's always good to have several opinions,

if you can get them, before making a decision.

just to be sure the uploaded AutoIt3.exe is incorporating the @progAndy

non bloking socket.

Just so that we are on the same page, the socket will be returned in

blocking mode per my last post on security concerns.

Within the function _TCPConnect(), non-blocking is only used for a

connection timeout - and then it is set back to blocking mode if

no errors or timeout.

Are we on the same page? Let me know if any questions.

---

Here's another matter I want to bring up on TCPRecv():

We could return all WSA errors as -1 and set @extended with the WSA error.

It's already been called, might as well put it to good use. This will also

save coders another call to WSAGetLastError().

It seems efficient to me. What do you think?

And I would like to hear members chime in too!

Don't be bashful.

Edited by ripdad

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

Link to comment
Share on other sites

Okay, here is the suggested code. It's the best I can do.

It needs to be scrutinized by a code pro, since I'm just

a hobbyist coder.

All the tests I performed with it, went well with no problems.

Included were loading large websites (including images),

downloading large files (mp3's, iso's, zip's) and streaming

1 hour videos.

I also posted this with these functions and TCPSend().

note: I have deleted my previous scripts in this topic to avoid confusion.

-edit-

deleted this script too.

Edited by ripdad

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

Link to comment
Share on other sites

jpm,

Good catch, I completely forgot to check -2.

There are actually several issues, now that you bring that up.

I still have a hard time accepting that blank is greater than -1.

And blank is equal to 0.

$blank = ''
$number = 0

MsgBox(0, '', $blank = $number)

$number = -1

MsgBox(0, '', $blank > $number)

I know, I know -- apples and oranges. It still gets me every once in a while!

That statement has been rewritten and it works now.

---

On to the other issues --

The FIONREAD call is not returning any errors if disconnected.

WSAGetLastError does not return anything either.

I was very disappointed that section didn't trigger the error.

All kinds of things was tried to save it, because it was a

great way to test if the socket had data in it, without making

a call to "recv". But if it won't trigger a disconnect error,

then we can't use it. What a shame.

So I tried "setsockopt" with the "TCP_NODELAY" option.

Same result -- it won't trigger a disconnect error.

There are three other methods at this point (that I know of).

The first is to use "select", and use a timeout on the socket.

Something like that is okay for TCPConnect(), because it is not

called in great amounts like TCPRecv. It might be a little

sluggish on slower internet connections, because of the calls

between timeouts, depending on what is set for it. Even 100ms

would be sluggish between calls. Light activity would probably be

no problem, but downloading a large file, could seem like dialup.

The second is leaving the socket in non-blocking mode, which was

discussed already.

The third is my first idea. Switch between blocking and non-blocking.

I ran some more tests again, and it works good. It still kinda bothers

me about the number of calls, switching back and forth between them.

Either one of the last two, triggers a disconnect with no problem.

So, it boils down to a possible security issue, which leaves the

socket in non-blocking mode and which is highly efficient or

switching between the two modes with a small hit on efficiency.

Unless someone has a better idea -- some code that can be tested,

I think this is what we are left with so far.

Now of course, everyone has an opinion on how to handle "recv"

calls. Some have a memory buffer that let the data stream into

it with one recv call, and then gather the data from that buffer.

I have seen several codes that do that on the internet. I'm not

familiar with memory code, since I have never needed it. Perhaps,

someone will step up to the plate on this -Or- some other way.

By the way, I'm curious how v3.3.8.1 handled disconnects.

No need to post code - just the highlights would be good.

I will wait for a few days to give time for response, to find

out if someone has another or better option. If not, then I

suppose the switching option is the best bet at this point.

In the meantime, I will be doing more research and peel away

some more of the onion to find a better option.

Edited by ripdad

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

Link to comment
Share on other sites

jpm,

I still don't understand how a ddlcall of an Int can return "" as error should return -1 and set WSAError

It doesn't happen all the time. Most of the time, it's -1.

I now see why the code at MSDN is written that way.

Seems it skips a beat every so often.

--

To make a long story short, everything I tried, either would not

trigger a disconnect error or had some kind of quirk or drawback,

like freezing a gui for a length a time. Even a timeout that was

set at 1ms, had issues.

I now know why ProgAndy coded _TCPConnect() with a timeout the

way he did. It would have issues otherwise.

The Only one that was trouble-free, is this code, posted below.

I think this is the best we are going to get. The icing on the

cake are the new features.

Just for clarity, this code should not break any scripts, since

@extended was not used in previous versions of AutoIt.

TCPRecv:

The only changes, are the @error values (as far as earlier versions

are concerned), which have a -1 socket error. The new one has two

more, -2 and -3. None of this should cause scripts to break.

TCPConnect, has more error codes, than previous versions.

That also should not be a problem, if scripts were coded

properly. If not, it's a minor thing to adjust.

The help file will have to change the reference from:

Windows API WSAGetError return value (see MSDN),

to something like:

For @extended WSAError values see MSDN.

The new @error and @extended values will need to be

added, along with the new features to each.

I have refined all functions in this script, including

the example script and the client/server functions. So,

all my old scripts you may have from this topic are now void.

Let me know if you have any questions. Thanks.

-edit-

deleted script

Edited by ripdad

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

Link to comment
Share on other sites

Thanks

Just slight comments before I go to AutoIt code proposal

I always want @error to be unique as you did but overriding an set @error is not too good (I don't like -7) anyway WSA_ERROR will not be set

Setting a local structure to 0 before return is not needed as the return will deallocated it.

Use "struct*", $t1 instead of creating a ptr to the structure

I attach a modification of your file taken in account my remarks, Just check in case you disagree

Nice Job I will now ti adapt TCPRecv() and perhaps TCPConnect()

Cheers

Link to comment
Share on other sites

I always want @error to be unique as you did but overriding an set @error is not too good (I don't like -7) anyway WSA_ERROR will not be set

I expected you would probably change a few things.

Setting a local structure to 0 before return is not needed as the return will deallocated it.

My reason for it is, that function will be pounded every 10ms

with various buffer sizes. I was concerned about a memory leak.

Use "struct*", $t1 instead of creating a ptr to the structure

$t1 is a variable I used in place of the original code that

ProgAndy wrote. He put the DllStructGetPtr() inside the

parameter.

I have it in $t1, to reduce the long code length, so I don't

have to scroll to the right.

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

Link to comment
Share on other sites

I was working on an event-driven non blocking TCP Serve/Client class for a while, now it's almost done.

Feel free to give it a try (I will post it in Example Scripts forum as son as i finished documenting the sources).

Looks like you put a lot of work into it. Thanks for the preview.

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

Link to comment
Share on other sites

JScript,

I played with TCPSend() with what you requested in non-blocking mode.

That will be problematic, but not impossible.

1. You would have to know the exact size of what you are sending.

2. You would have to use a loop with a timeout to escape if something

went wrong along with an @error check for a disconnect. Otherwise,

once you have the return sent size, then you could exit the loop.

I will probably play with it some more and see if I run into other

issues, in a few days or so.

Edited by ripdad

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

Link to comment
Share on other sites

My reason for it is, that function will be pounded every 10ms

with various buffer sizes. I was concerned about a memory leak.

 

I miss the point no memory leak should occur when returning from Functio which defined a local structure

as the return will release the structure no need to assign the local structure to an int to release it

Link to comment
Share on other sites

jpm,

I revised the code with your suggestions.

AutoIt v3.3.8.1+

---

JScript,

Give this a test run and let me know what you think.

Examples are at the bottom of the script.

_WSA_TCPRecv-20140703.au3

; Filename: _WSA_TCPRecv-20140703.au3
; Version.: AutoIt v3.3.8.1+
; Date....: July 03, 2014

Opt("TrayIconDebug", 1)

Global $bWSA = True

If Not TCPStartup() Then
    Exit MsgBox(8208, '', 'Error: TCPStartup' & @TAB, 5)
EndIf

Global Const $hWS2_32 = DllOpen(@WindowsDir & '\system32\ws2_32.dll')
If $hWS2_32 = -1 Then Exit

OnAutoItExitRegister('_WSA_ShutDown')

Global Const $strIP = @IPAddress1
Global Const $nPort = 8091

Global Const $Connection = TCPListen($strIP, $nPort)
If @error Then
    _TCP_Client()
Else
    _TCP_Server()
EndIf
Exit
;
Func _TCP_Client()
    Local $nSocket = _TCPConnect($strIP, $nPort)
    If @error Or ($nSocket < 1) Then Return

    _WSA_TCPSend($nSocket, 'SEND_DATA_MARY')
    If @error Then
        TCPCloseSocket($nSocket)
        Return
    EndIf

    Local $nBytes = 0, $nError = 0, $nWSAError = 0, $string = ''

    Do
        Sleep(10)

        If $bWSA Then
            $string &= _WSA_TCPRecv($nSocket, 32)
        Else
            $string &= TCPRecv($nSocket, 32)
        EndIf

        $nError = @error
        If $nError Then
            $nWSAError = @extended
        Else
            $nBytes += @extended
        EndIf
    Until $nError

    Local $sResult = ''
    $sResult &= 'AutoIt v' & @AutoItVersion & @CRLF & @CRLF
    $sResult &= '@error: ' & $nError & @CRLF
    $sResult &= 'WSAError: ' & $nWSAError & @CRLF
    $sResult &= 'Bytes Received: ' & $nBytes & @CRLF & @CRLF
    $sResult &= $string & @CRLF

    TCPCloseSocket($nSocket)
    MsgBox(0, '[Client] - Results From Server', $sResult)
EndFunc
;
Func _TCP_Server()
    Local $nSocket, $string = ''

    MsgBox(8256, 'Status', 'Server Online' & @TAB, 1)

    Do
        Sleep(100)
        $nSocket = TCPAccept($Connection)
    Until $nSocket > -1

    Do
        Sleep(10)
        If $string Then ExitLoop

        If $bWSA Then
            $string &= _WSA_TCPRecv($nSocket, 32)
        Else
            $string &= TCPRecv($nSocket, 32)
        EndIf
    Until @error

    Switch $string
        Case 'SEND_DATA_MARY'
            Local $str = 'Mary had a little lamb, whose fleece was white as snow;' & @CRLF
            $str &= 'and everywhere that Mary went, the lamb was sure to go.' & @CRLF
            _WSA_TCPSend($nSocket, $str)
        Case Else
    EndSwitch

    Sleep(100)
    TCPCloseSocket($nSocket)
    Sleep(100)
    MsgBox(8256, '[Server] - Command From Client', $string)
EndFunc
;
;======================================================================================
;#FUNCTION#....:
; Name.........: _WSA_TCPRecv()
;..............:
; Version......: AutoIt v3.3.8.1+
;..............:
; Dependencies.: _WSA_GetLastError() and a handle to ws2_32.dll
;..............:
; @error.......;  0 no error.
;..............: -1 socket error.
;..............: -2 disconnected.
;..............: -3 dll error.
;..............:
; @extended....; If @error Then returns a WSA Error Code.
;............. : Else returns number of bytes received.
;..............:
; Return Value.: If Not @error And @extended > 0 Then returns data.
;..............: Else returns blank.
;..............:
; Remarks......: Link to WSA Error Codes:
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
;======================================================================================
Func _WSA_TCPRecv(ByRef $nSocket, $nMaxLen = 4096, $nBinaryMode = 0)
    ;[set non-blocking mode]
    Local $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $nSocket, 'long', 0x8004667E, 'ulong*', 1)
    If @error Then
        Return SetError(-3, 0, ''); dll error
    ElseIf ($aResult[0] <> 0) Then
        Return SetError(-1, _WSA_GetLastError(), ''); socket error
    EndIf; WSAGetLastError will return socket errors with this statement (example: 10038-invalid socket).

    ;[get buffer type]
    If $nBinaryMode Then
        Local $tBuffer = DllStructCreate('byte[' & $nMaxLen & ']')
    Else
        Local $tBuffer = DllStructCreate('char buffer[' & $nMaxLen & ']')
    EndIf

    ;[receive data]
    $aResult = DllCall($hWS2_32, 'int', 'recv', 'int', $nSocket, 'struct*', $tBuffer, 'int', $nMaxLen, 'int', 0)
    If @error Then
        Return SetError(-3, _WSA_GetLastError(), ''); dll error
    EndIf

    ;[check WSA error]
    Local $nError = _WSA_GetLastError()
    If ($nError <> 0) And ($nError <> 10035) Then; <- WSAEWOULDBLOCK (non-blocking socket (non-fatal error))
        Return SetError(-1, $nError, ''); socket error
    EndIf

    Local $nBytes = $aResult[0]

    ;[set blocking mode]
    $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $nSocket, 'long', 0x8004667E, 'ulong*', 0)
    If @error Then
        Return SetError(-3, 0, ''); dll error
    ElseIf ($aResult[0] <> 0) Then
        Return SetError(-1, _WSA_GetLastError(), ''); socket error
    EndIf

    Local $sData = ''
    $nError = 0

    ;[process results]
    If ($nBytes > 0) Then; data received
        $sData = DllStructGetData($tBuffer, 1)
        If $nBinaryMode Then; extract binary data
            $sData = StringLeft($sData, ($nBytes * 2) + 2)
        Else; extract raw data
            $sData = StringLeft($sData, $nBytes)
        EndIf
    ElseIf ($nBytes == 0) Then; disconnected
        $nError = -2
    Else
        $nBytes = 0; no data received
    EndIf

    Return SetError($nError, $nBytes, $sData)
EndFunc
;
;======================================================================================
;#FUNCTION#....:
; Name.........: _WSA_TCPSend()
;..............:
; Version......: AutoIt v3.3.8.1+
;..............:
; Dependencies.: _WSA_GetLastError() and a handle to ws2_32.dll
;..............:
; @error.......;  0 no error.
;..............: -1 socket error.
;..............: -2 disconnected.
;..............: -3 dll error.
;..............:
; @extended....; If @error Then @extended returns a WSA Error Code.
;............. : Else returns 0.
;..............:
; Return Value.: If Not @error Then returns number of bytes sent.
;..............: Else returns 0.
;..............:
; Remarks......: Link to WSA Error Codes:
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
;======================================================================================
Func _WSA_TCPSend(ByRef $nSocket, $sData)
    ;[set non-blocking mode]
    Local $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $nSocket, 'long', 0x8004667E, 'ulong*', 1)
    If @error Then
        Return SetError(-3, 0, 0); dll error
    ElseIf ($aResult[0] <> 0) Then
        Return SetError(-1, _WSA_GetLastError(), 0); socket error
    EndIf; WSAGetLastError will return socket errors with this statement (ie: 10038-invalid socket).

    Local $nBytes = StringLen($sData)
    Local $tBuffer = DllStructCreate('char buffer[' & $nBytes & ']')
    DllStructSetData($tBuffer, 1, $sData)

    ;[send data]
    $aResult = DllCall($hWS2_32, 'int', 'send', 'int', $nSocket, 'struct*', $tBuffer, 'int', $nBytes, 'int', 0)
    If @error Then
        Return SetError(-3, 0, 0); dll error
    EndIf

    ;[check WSA error]
    Local $nError = _WSA_GetLastError()
    If ($nError <> 0) And ($nError <> 10035) Then; <- WSAEWOULDBLOCK (non-blocking socket (non-fatal error))
        Return SetError(-1, $nError, 0); socket error
    EndIf

    $nBytes = $aResult[0]

    ;[set blocking mode]
    $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $nSocket, 'long', 0x8004667E, 'ulong*', 0)
    If @error Then
        Return SetError(-3, 0, 0); dll error
    ElseIf ($aResult[0] <> 0) Then
        Return SetError(-1, _WSA_GetLastError(), 0); socket error
    EndIf

    $nError = 0

    ;[process results]
    If ($nBytes > 0) Then; bytes sent
        ; pass through
    ElseIf ($nBytes == 0) Then; disconnected
        $nError = -2
    Else
        $nBytes = 0; no bytes sent
    EndIf

    Return SetError($nError, 0, $nBytes)
EndFunc
;
;====================================================================================
;#FUNCTION#
; Name........: _TCPConnect()
;
; This is a rewrite of the original code - all credits go to ProgAndy and JScript.
; Purpose: format is easier to debug and maintain.
;
; Original Link:
; http://www.autoitscript.com/forum/topic/127415-tcpconnect-sipaddr-iport-itimeout/
;..............:
; Version......: AutoIt v3.3.8.1+
;..............:
; Description..: Attempts a socket connection with a timeout.
;..............:
; Dependencies.: _WSA_GetLastError() and a handle to ws2_32.dll
;..............:
; @error.......;  0 no error - socket is returned in blocking mode.
;..............: -1 could not create socket.
;..............: -2 ip incorrect.
;..............: -3 could not get port.
;..............: -4 could not set blocking or non-blocking mode.
;..............: -5 could not connect.
;..............: -6 timed out.
;..............: -7 dll error.
; .............:
; @extended....; If @error Then returns a WSA Error Code.
;..............: Else returns 0.
;..............:
; Return Value.: If @error Then returns -1.
;..............: Else returns socket.
;..............:
; Remarks......: Link to WSA Error Codes:
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
;====================================================================================
Func _TCPConnect($sIPAddr, $iPort, $iTimeOut = 3000); <- default: 3 seconds
    ;[create socket]
    Local $hSock = DllCall($hWS2_32, 'uint', 'socket', 'int', 2, 'int', 1, 'int', 6); AF_INET, SOCK_STREAM, IPPROTO_TCP
    If @error Then
        Return SetError(-7, 0, -1); dll error
    ElseIf ($hSock[0] == -1) Or ($hSock[0] == 4294967295) Then; 4294967295 = 0xFFFFFFFF
        Return SetError(-1, _WSA_GetLastError(), -1); could not create socket
    EndIf
    $hSock = $hSock[0]

    ;[get ip handle]
    Local $aIP = DllCall($hWS2_32, 'ulong', 'inet_addr', 'str', $sIPAddr)
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aIP[0] == -1) Or ($aIP[0] == 4294967295) Then
        TCPCloseSocket($hSock)
        Return SetError(-2, _WSA_GetLastError(), -1); ip incorrect
    EndIf

    ;[get port handle]
    Local $aPort = DllCall($hWS2_32, 'ushort', 'htons', 'ushort', $iPort)
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aPort[0] < 1) Then
        TCPCloseSocket($hSock)
        Return SetError(-3, _WSA_GetLastError(), -1); could not get port
    EndIf

    ;[set socket to non-blocking mode for timeout]
    Local $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $hSock, 'long', 0x8004667E, 'ulong*', 1); FIONBIO, NON-BLOCKING
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aResult[0] <> 0) Then
        TCPCloseSocket($hSock)
        Return SetError(-4, _WSA_GetLastError(), -1); socket error (could not set non-blocking mode)
    EndIf

    ;[set binding]
    Local $tSockAddr = DllStructCreate('short sin_family;ushort sin_port;ulong sin_addr;char sin_zero[8];')
    DllStructSetData($tSockAddr, 1, 2)
    DllStructSetData($tSockAddr, 2, $aPort[0])
    DllStructSetData($tSockAddr, 3, $aIP[0])

    ;[attempt connect]
    $aResult = DllCall($hWS2_32, 'int', 'connect', 'int', $hSock, 'struct*', $tSockAddr, 'int', DllStructGetSize($tSockAddr))
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aResult[0] <> 0) And (_WSA_GetLastError() <> 10035) Then; <- WSAEWOULDBLOCK - non-blocking socket (no data queued on socket - non-fatal error)
        TCPCloseSocket($hSock)
        Return SetError(-5, _WSA_GetLastError(), -1); could not connect
    EndIf

    ;[set timeout]
    Local $t1 = DllStructCreate('uint;int')
    DllStructSetData($t1, 1, 1)
    DllStructSetData($t1, 2, $hSock)

    Local $t2 = DllStructCreate('long;long')
    DllStructSetData($t2, 1, Floor($iTimeOut / 1000))
    DllStructSetData($t2, 2, Mod($iTimeOut, 1000))

    ;[init timeout]
    $aResult = DllCall($hWS2_32, 'int', 'select', 'int', $hSock, 'struct*', $t1, 'struct*', $t1, 'ptr', 0, 'struct*', $t2)
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aResult[0] == 0) Then
        TCPCloseSocket($hSock)
        Return SetError(-6, _WSA_GetLastError(), -1); timed out
    EndIf

    ;[set socket to blocking mode again]
    Local $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $hSock, 'long', 0x8004667E, 'ulong*', 0); FIONBIO, BLOCKING
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aResult[0] <> 0) Then
        TCPCloseSocket($hSock)
        Return SetError(-4, _WSA_GetLastError(), -1); socket error (could not set blocking mode)
    EndIf

    Return SetError(0, 0, $hSock)
EndFunc
;
Func _WSA_GetLastError()
    Local $aResult = DllCall($hWS2_32, 'int', 'WSAGetLastError')
    If @error Then
        Return SetError(-1, 0, -1); dll error
    Else
        Return SetError(0, 0, $aResult[0])
    EndIf
EndFunc
;
Func _WSA_ShutDown()
    DllClose($hWS2_32)
    TCPShutdown()
    Exit
EndFunc
;
;==============================
;
;    recv example function
;
;==============================
Func _TCPRecv_Example(ByRef $nSocket)
    Local $nExtended, $nError, $sRecv
    Local $nTotalBytes = 0, $sData = ''

    For $i = 1 To 100; <-- serves as a timeout scheme and will exit loop if it reaches 100. (approximately ~1500 ms)
        $sRecv = _WSA_TCPRecv($nSocket, 16384); the majority of web server sends is 8192 bytes, but we'll double it for a little extra.
        $nError = @error
        $nExtended = @extended

        If $nError Then
            Return SetError($nError, $nExtended, '')
        ElseIf $nExtended Then; do something with data
            $nTotalBytes += $nExtended; add received bytes to total bytes
            $sData &= $sRecv; gather incoming data
            $i = 0; reset timeout
        Else; no data received
            If ($i > 30) And $nTotalBytes Then; accelerate the timeout when no further data is received
                ExitLoop
            EndIf
        EndIf

        Sleep(10); part cpu breather and part timeout scheme
    Next

    If $nTotalBytes Then; do something with total bytes and resulting data
        Return SetError(0, $nTotalBytes, $sData)
    Else
        Return SetError(1, 0, ''); timed out
    EndIf
EndFunc
;
;==============================
;
;    send example function
;
;==============================
Func _TCPSend_Example(ByRef $nSocket, $sData)
    Local $nTotalBytes = StringLen($sData)
    Local $nExtended, $nError, $nBytes

    For $i = 1 To 100; <-- serves as a timeout scheme and will exit loop if it reaches 100. (approximately ~1500 ms)
        $nBytes = _WSA_TCPSend($nSocket, $sData)
        $nError = @error
        $nExtended = @extended

        If $nError Then
            Return SetError($nError, $nExtended, 0)
        ElseIf ($nBytes == $nTotalBytes) Then
            Return SetError(0, 0, 1); success
        ElseIf ($nBytes > 0) Then
            Return SetError(2, 0, 0); failed to send total byte count. (partial send)
        Else
            ; pass through till timeout or bytes sent.
        EndIf

        Sleep(10); part cpu breather and part timeout scheme
    Next

    Return SetError(1, 0, 0); timed out
EndFunc
;

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

Link to comment
Share on other sites

@ripdad

I have tested and it worked well, but I'll have run tests on my remote desktop program because then I can evaluate the mode functions in non-bloking

JS

http://forum.autoitbrasil.com/ (AutoIt v3 Brazil!!!)

Somewhere Out ThereJames Ingram

somewh10.png

dropbo10.pngDownload Dropbox - Simplify your life!
Your virtual HD wherever you go, anywhere!

Link to comment
Share on other sites

jpm,

I revised the code with your suggestions.

AutoIt v3.3.8.1+

---

JScript,

Give this a test run and let me know what you think.

Examples are at the bottom of the script.

attachicon.gif_WSA_TCPRecv-20140703.au3

; Filename: _WSA_TCPRecv-20140703.au3
; Version.: AutoIt v3.3.8.1+
; Date....: July 03, 2014

Opt("TrayIconDebug", 1)

Global $bWSA = True

If Not TCPStartup() Then
    Exit MsgBox(8208, '', 'Error: TCPStartup' & @TAB, 5)
EndIf

Global Const $hWS2_32 = DllOpen(@WindowsDir & '\system32\ws2_32.dll')
If $hWS2_32 = -1 Then Exit

OnAutoItExitRegister('_WSA_ShutDown')

Global Const $strIP = @IPAddress1
Global Const $nPort = 8091

Global Const $Connection = TCPListen($strIP, $nPort)
If @error Then
    _TCP_Client()
Else
    _TCP_Server()
EndIf
Exit
;
Func _TCP_Client()
    Local $nSocket = _TCPConnect($strIP, $nPort)
    If @error Or ($nSocket < 1) Then Return

    _WSA_TCPSend($nSocket, 'SEND_DATA_MARY')
    If @error Then
        TCPCloseSocket($nSocket)
        Return
    EndIf

    Local $nBytes = 0, $nError = 0, $nWSAError = 0, $string = ''

    Do
        Sleep(10)

        If $bWSA Then
            $string &= _WSA_TCPRecv($nSocket, 32)
        Else
            $string &= TCPRecv($nSocket, 32)
        EndIf

        $nError = @error
        If $nError Then
            $nWSAError = @extended
        Else
            $nBytes += @extended
        EndIf
    Until $nError

    Local $sResult = ''
    $sResult &= 'AutoIt v' & @AutoItVersion & @CRLF & @CRLF
    $sResult &= '@error: ' & $nError & @CRLF
    $sResult &= 'WSAError: ' & $nWSAError & @CRLF
    $sResult &= 'Bytes Received: ' & $nBytes & @CRLF & @CRLF
    $sResult &= $string & @CRLF

    TCPCloseSocket($nSocket)
    MsgBox(0, '[Client] - Results From Server', $sResult)
EndFunc
;
Func _TCP_Server()
    Local $nSocket, $string = ''

    MsgBox(8256, 'Status', 'Server Online' & @TAB, 1)

    Do
        Sleep(100)
        $nSocket = TCPAccept($Connection)
    Until $nSocket > -1

    Do
        Sleep(10)
        If $string Then ExitLoop

        If $bWSA Then
            $string &= _WSA_TCPRecv($nSocket, 32)
        Else
            $string &= TCPRecv($nSocket, 32)
        EndIf
    Until @error

    Switch $string
        Case 'SEND_DATA_MARY'
            Local $str = 'Mary had a little lamb, whose fleece was white as snow;' & @CRLF
            $str &= 'and everywhere that Mary went, the lamb was sure to go.' & @CRLF
            _WSA_TCPSend($nSocket, $str)
        Case Else
    EndSwitch

    Sleep(100)
    TCPCloseSocket($nSocket)
    Sleep(100)
    MsgBox(8256, '[Server] - Command From Client', $string)
EndFunc
;
;======================================================================================
;#FUNCTION#....:
; Name.........: _WSA_TCPRecv()
;..............:
; Version......: AutoIt v3.3.8.1+
;..............:
; Dependencies.: _WSA_GetLastError() and a handle to ws2_32.dll
;..............:
; @error.......;  0 no error.
;..............: -1 socket error.
;..............: -2 disconnected.
;..............: -3 dll error.
;..............:
; @extended....; If @error Then returns a WSA Error Code.
;............. : Else returns number of bytes received.
;..............:
; Return Value.: If Not @error And @extended > 0 Then returns data.
;..............: Else returns blank.
;..............:
; Remarks......: Link to WSA Error Codes:
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
;======================================================================================
Func _WSA_TCPRecv(ByRef $nSocket, $nMaxLen = 4096, $nBinaryMode = 0)
    ;[set non-blocking mode]
    Local $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $nSocket, 'long', 0x8004667E, 'ulong*', 1)
    If @error Then
        Return SetError(-3, 0, ''); dll error
    ElseIf ($aResult[0] <> 0) Then
        Return SetError(-1, _WSA_GetLastError(), ''); socket error
    EndIf; WSAGetLastError will return socket errors with this statement (example: 10038-invalid socket).

    ;[get buffer type]
    If $nBinaryMode Then
        Local $tBuffer = DllStructCreate('byte[' & $nMaxLen & ']')
    Else
        Local $tBuffer = DllStructCreate('char buffer[' & $nMaxLen & ']')
    EndIf

    ;[receive data]
    $aResult = DllCall($hWS2_32, 'int', 'recv', 'int', $nSocket, 'struct*', $tBuffer, 'int', $nMaxLen, 'int', 0)
    If @error Then
        Return SetError(-3, _WSA_GetLastError(), ''); dll error
    EndIf

    ;[check WSA error]
    Local $nError = _WSA_GetLastError()
    If ($nError <> 0) And ($nError <> 10035) Then; <- WSAEWOULDBLOCK (non-blocking socket (non-fatal error))
        Return SetError(-1, $nError, ''); socket error
    EndIf

    Local $nBytes = $aResult[0]

    ;[set blocking mode]
    $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $nSocket, 'long', 0x8004667E, 'ulong*', 0)
    If @error Then
        Return SetError(-3, 0, ''); dll error
    ElseIf ($aResult[0] <> 0) Then
        Return SetError(-1, _WSA_GetLastError(), ''); socket error
    EndIf

    Local $sData = ''
    $nError = 0

    ;[process results]
    If ($nBytes > 0) Then; data received
        $sData = DllStructGetData($tBuffer, 1)
        If $nBinaryMode Then; extract binary data
            $sData = StringLeft($sData, ($nBytes * 2) + 2)
        Else; extract raw data
            $sData = StringLeft($sData, $nBytes)
        EndIf
    ElseIf ($nBytes == 0) Then; disconnected
        $nError = -2
    Else
        $nBytes = 0; no data received
    EndIf

    Return SetError($nError, $nBytes, $sData)
EndFunc
;
;======================================================================================
;#FUNCTION#....:
; Name.........: _WSA_TCPSend()
;..............:
; Version......: AutoIt v3.3.8.1+
;..............:
; Dependencies.: _WSA_GetLastError() and a handle to ws2_32.dll
;..............:
; @error.......;  0 no error.
;..............: -1 socket error.
;..............: -2 disconnected.
;..............: -3 dll error.
;..............:
; @extended....; If @error Then @extended returns a WSA Error Code.
;............. : Else returns 0.
;..............:
; Return Value.: If Not @error Then returns number of bytes sent.
;..............: Else returns 0.
;..............:
; Remarks......: Link to WSA Error Codes:
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
;======================================================================================
Func _WSA_TCPSend(ByRef $nSocket, $sData)
    ;[set non-blocking mode]
    Local $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $nSocket, 'long', 0x8004667E, 'ulong*', 1)
    If @error Then
        Return SetError(-3, 0, 0); dll error
    ElseIf ($aResult[0] <> 0) Then
        Return SetError(-1, _WSA_GetLastError(), 0); socket error
    EndIf; WSAGetLastError will return socket errors with this statement (ie: 10038-invalid socket).

    Local $nBytes = StringLen($sData)
    Local $tBuffer = DllStructCreate('char buffer[' & $nBytes & ']')
    DllStructSetData($tBuffer, 1, $sData)

    ;[send data]
    $aResult = DllCall($hWS2_32, 'int', 'send', 'int', $nSocket, 'struct*', $tBuffer, 'int', $nBytes, 'int', 0)
    If @error Then
        Return SetError(-3, 0, 0); dll error
    EndIf

    ;[check WSA error]
    Local $nError = _WSA_GetLastError()
    If ($nError <> 0) And ($nError <> 10035) Then; <- WSAEWOULDBLOCK (non-blocking socket (non-fatal error))
        Return SetError(-1, $nError, 0); socket error
    EndIf

    $nBytes = $aResult[0]

    ;[set blocking mode]
    $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $nSocket, 'long', 0x8004667E, 'ulong*', 0)
    If @error Then
        Return SetError(-3, 0, 0); dll error
    ElseIf ($aResult[0] <> 0) Then
        Return SetError(-1, _WSA_GetLastError(), 0); socket error
    EndIf

    $nError = 0

    ;[process results]
    If ($nBytes > 0) Then; bytes sent
        ; pass through
    ElseIf ($nBytes == 0) Then; disconnected
        $nError = -2
    Else
        $nBytes = 0; no bytes sent
    EndIf

    Return SetError($nError, 0, $nBytes)
EndFunc
;
;====================================================================================
;#FUNCTION#
; Name........: _TCPConnect()
;
; This is a rewrite of the original code - all credits go to ProgAndy and JScript.
; Purpose: format is easier to debug and maintain.
;
; Original Link:
; http://www.autoitscript.com/forum/topic/127415-tcpconnect-sipaddr-iport-itimeout/
;..............:
; Version......: AutoIt v3.3.8.1+
;..............:
; Description..: Attempts a socket connection with a timeout.
;..............:
; Dependencies.: _WSA_GetLastError() and a handle to ws2_32.dll
;..............:
; @error.......;  0 no error - socket is returned in blocking mode.
;..............: -1 could not create socket.
;..............: -2 ip incorrect.
;..............: -3 could not get port.
;..............: -4 could not set blocking or non-blocking mode.
;..............: -5 could not connect.
;..............: -6 timed out.
;..............: -7 dll error.
; .............:
; @extended....; If @error Then returns a WSA Error Code.
;..............: Else returns 0.
;..............:
; Return Value.: If @error Then returns -1.
;..............: Else returns socket.
;..............:
; Remarks......: Link to WSA Error Codes:
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
;====================================================================================
Func _TCPConnect($sIPAddr, $iPort, $iTimeOut = 3000); <- default: 3 seconds
    ;[create socket]
    Local $hSock = DllCall($hWS2_32, 'uint', 'socket', 'int', 2, 'int', 1, 'int', 6); AF_INET, SOCK_STREAM, IPPROTO_TCP
    If @error Then
        Return SetError(-7, 0, -1); dll error
    ElseIf ($hSock[0] == -1) Or ($hSock[0] == 4294967295) Then; 4294967295 = 0xFFFFFFFF
        Return SetError(-1, _WSA_GetLastError(), -1); could not create socket
    EndIf
    $hSock = $hSock[0]

    ;[get ip handle]
    Local $aIP = DllCall($hWS2_32, 'ulong', 'inet_addr', 'str', $sIPAddr)
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aIP[0] == -1) Or ($aIP[0] == 4294967295) Then
        TCPCloseSocket($hSock)
        Return SetError(-2, _WSA_GetLastError(), -1); ip incorrect
    EndIf

    ;[get port handle]
    Local $aPort = DllCall($hWS2_32, 'ushort', 'htons', 'ushort', $iPort)
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aPort[0] < 1) Then
        TCPCloseSocket($hSock)
        Return SetError(-3, _WSA_GetLastError(), -1); could not get port
    EndIf

    ;[set socket to non-blocking mode for timeout]
    Local $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $hSock, 'long', 0x8004667E, 'ulong*', 1); FIONBIO, NON-BLOCKING
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aResult[0] <> 0) Then
        TCPCloseSocket($hSock)
        Return SetError(-4, _WSA_GetLastError(), -1); socket error (could not set non-blocking mode)
    EndIf

    ;[set binding]
    Local $tSockAddr = DllStructCreate('short sin_family;ushort sin_port;ulong sin_addr;char sin_zero[8];')
    DllStructSetData($tSockAddr, 1, 2)
    DllStructSetData($tSockAddr, 2, $aPort[0])
    DllStructSetData($tSockAddr, 3, $aIP[0])

    ;[attempt connect]
    $aResult = DllCall($hWS2_32, 'int', 'connect', 'int', $hSock, 'struct*', $tSockAddr, 'int', DllStructGetSize($tSockAddr))
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aResult[0] <> 0) And (_WSA_GetLastError() <> 10035) Then; <- WSAEWOULDBLOCK - non-blocking socket (no data queued on socket - non-fatal error)
        TCPCloseSocket($hSock)
        Return SetError(-5, _WSA_GetLastError(), -1); could not connect
    EndIf

    ;[set timeout]
    Local $t1 = DllStructCreate('uint;int')
    DllStructSetData($t1, 1, 1)
    DllStructSetData($t1, 2, $hSock)

    Local $t2 = DllStructCreate('long;long')
    DllStructSetData($t2, 1, Floor($iTimeOut / 1000))
    DllStructSetData($t2, 2, Mod($iTimeOut, 1000))

    ;[init timeout]
    $aResult = DllCall($hWS2_32, 'int', 'select', 'int', $hSock, 'struct*', $t1, 'struct*', $t1, 'ptr', 0, 'struct*', $t2)
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aResult[0] == 0) Then
        TCPCloseSocket($hSock)
        Return SetError(-6, _WSA_GetLastError(), -1); timed out
    EndIf

    ;[set socket to blocking mode again]
    Local $aResult = DllCall($hWS2_32, 'int', 'ioctlsocket', 'int', $hSock, 'long', 0x8004667E, 'ulong*', 0); FIONBIO, BLOCKING
    If @error Then
        TCPCloseSocket($hSock)
        Return SetError(-7, 0, -1); dll error
    ElseIf ($aResult[0] <> 0) Then
        TCPCloseSocket($hSock)
        Return SetError(-4, _WSA_GetLastError(), -1); socket error (could not set blocking mode)
    EndIf

    Return SetError(0, 0, $hSock)
EndFunc
;
Func _WSA_GetLastError()
    Local $aResult = DllCall($hWS2_32, 'int', 'WSAGetLastError')
    If @error Then
        Return SetError(-1, 0, -1); dll error
    Else
        Return SetError(0, 0, $aResult[0])
    EndIf
EndFunc
;
Func _WSA_ShutDown()
    DllClose($hWS2_32)
    TCPShutdown()
    Exit
EndFunc
;
;==============================
;
;    recv example function
;
;==============================
Func _TCPRecv_Example(ByRef $nSocket)
    Local $nExtended, $nError, $sRecv
    Local $nTotalBytes = 0, $sData = ''

    For $i = 1 To 100; <-- serves as a timeout scheme and will exit loop if it reaches 100. (approximately ~1500 ms)
        $sRecv = _WSA_TCPRecv($nSocket, 16384); the majority of web server sends is 8192 bytes, but we'll double it for a little extra.
        $nError = @error
        $nExtended = @extended

        If $nError Then
            Return SetError($nError, $nExtended, '')
        ElseIf $nExtended Then; do something with data
            $nTotalBytes += $nExtended; add received bytes to total bytes
            $sData &= $sRecv; gather incoming data
            $i = 0; reset timeout
        Else; no data received
            If ($i > 30) And $nTotalBytes Then; accelerate the timeout when no further data is received
                ExitLoop
            EndIf
        EndIf

        Sleep(10); part cpu breather and part timeout scheme
    Next

    If $nTotalBytes Then; do something with total bytes and resulting data
        Return SetError(0, $nTotalBytes, $sData)
    Else
        Return SetError(1, 0, ''); timed out
    EndIf
EndFunc
;
;==============================
;
;    send example function
;
;==============================
Func _TCPSend_Example(ByRef $nSocket, $sData)
    Local $nTotalBytes = StringLen($sData)
    Local $nExtended, $nError, $nBytes

    For $i = 1 To 100; <-- serves as a timeout scheme and will exit loop if it reaches 100. (approximately ~1500 ms)
        $nBytes = _WSA_TCPSend($nSocket, $sData)
        $nError = @error
        $nExtended = @extended

        If $nError Then
            Return SetError($nError, $nExtended, 0)
        ElseIf ($nBytes == $nTotalBytes) Then
            Return SetError(0, 0, 1); success
        ElseIf ($nBytes > 0) Then
            Return SetError(2, 0, 0); failed to send total byte count. (partial send)
        Else
            ; pass through till timeout or bytes sent.
        EndIf

        Sleep(10); part cpu breather and part timeout scheme
    Next

    Return SetError(1, 0, 0); timed out
EndFunc
;

Now I need to look at the send function

Cheers

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...