ripdad Posted June 27, 2014 Share Posted June 27, 2014 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 More sharing options...
jpm Posted June 28, 2014 Share Posted June 28, 2014 Yours and Funkey one Could you be a little more specific? What two approaches? Thanks. Yours and funkey one Link to comment Share on other sites More sharing options...
ripdad Posted June 28, 2014 Share Posted June 28, 2014 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 More sharing options...
jpm Posted June 28, 2014 Share Posted June 28, 2014 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 More sharing options...
ripdad Posted June 29, 2014 Share Posted June 29, 2014 (edited) 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 June 29, 2014 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 More sharing options...
ripdad Posted June 29, 2014 Share Posted June 29, 2014 (edited) 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 July 2, 2014 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 More sharing options...
jpm Posted June 29, 2014 Share Posted June 29, 2014 Hi, I try to use your latest script but disconnect -2 does not seems to fire. I use your old client/server scrpit with your new UDF can you check my script? Thanks _WSA_TCPRecv-20140629-jpm.au3 Link to comment Share on other sites More sharing options...
ripdad Posted June 30, 2014 Share Posted June 30, 2014 (edited) 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 June 30, 2014 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 More sharing options...
jpm Posted June 30, 2014 Share Posted June 30, 2014 I still don't understand how a ddlcall of an Int can return "" as error should return -1 and set WSAError I will wait your testing Thanks again Link to comment Share on other sites More sharing options...
ripdad Posted July 2, 2014 Share Posted July 2, 2014 (edited) 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 July 3, 2014 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 More sharing options...
jpm Posted July 2, 2014 Share Posted July 2, 2014 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 More sharing options...
FaridAgl Posted July 2, 2014 Share Posted July 2, 2014 (edited) 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). TCP Class.zip Edited July 2, 2014 by FaridAgl http://faridaghili.ir Link to comment Share on other sites More sharing options...
ripdad Posted July 2, 2014 Share Posted July 2, 2014 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 More sharing options...
ripdad Posted July 2, 2014 Share Posted July 2, 2014 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 More sharing options...
ripdad Posted July 2, 2014 Share Posted July 2, 2014 (edited) 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 July 2, 2014 by ripdad JScript 1 "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 More sharing options...
JScript Posted July 2, 2014 Author Share Posted July 2, 2014 @ripdad Ok friend, thanks for everything! I'll be watching this topic.JS http://forum.autoitbrasil.com/ (AutoIt v3 Brazil!!!) Somewhere Out ThereJames Ingram Download Dropbox - Simplify your life!Your virtual HD wherever you go, anywhere! Link to comment Share on other sites More sharing options...
jpm Posted July 3, 2014 Share Posted July 3, 2014 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 More sharing options...
ripdad Posted July 3, 2014 Share Posted July 3, 2014 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.au3expandcollapse popup; 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 More sharing options...
JScript Posted July 4, 2014 Author Share Posted July 4, 2014 @ripdadI 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-blokingJS http://forum.autoitbrasil.com/ (AutoIt v3 Brazil!!!) Somewhere Out ThereJames Ingram Download Dropbox - Simplify your life!Your virtual HD wherever you go, anywhere! Link to comment Share on other sites More sharing options...
jpm Posted July 4, 2014 Share Posted July 4, 2014 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 expandcollapse popup; 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 More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now