Jump to content

How can AutoIT manage many users?


Recommended Posts

hello to everybody!

yesterday, I found this post: '?do=embed' frameborder='0' data-embedContent>>

... where that server can accept requests from many users.

I tried to make my code more or less like that but it doesn't work.

now, telling you I tried but I didn't understand it so well, how can the code serves a client and listen to new connections at the same time?

I guess AutoIT has a single thread, so if it is doing a thing, can't do one more.

can you help me to understand how it can do?

thank you so much :bye:

ehi ehi ehi, what is your name?

Link to comment
Share on other sites

Here is an advanced example, you can send commands which will be separately interpreted.
 
A few notes: To detect the commands (or packets, whatever you call them), I set a header code and a footer code : {SOD}, {EOD} (for Start Of Data and End Of Data).
These codes mustn't be present in the command itself, otherwise the command won't be correctly interpreted.
If someone need a workaround on this, he can add the length of the command inside the header code e.g : {SOD:382} so that it will take the next 382 chars (then a footer code is not needed).
 
Server:

#include <WindowsConstants.au3>
#include <GUIConstants.au3>
#include <Constants.au3>
#include <GUIListView.au3>
#include <GUIEdit.au3>
#include <GUIMenu.au3>
#include <Date.au3>
#include <String.au3>
 
Example()
 
Func Example()
    ; Start the TCP service.
    TCPStartup()
 
    ; Register OnAutoItExit to be called when the script is closed.
    OnAutoItExitRegister("OnAutoItExit")
 
    ; Set the maximum of clients (and pending connections).
    Global Const $_iMaxClients = 100
 
    ; Assign a Local variable the socket and bind to the IP Address and Port specified with a maximum of $_iMaxClients pending connexions.
    Local $iListenSocket = TCPListen(@IPAddress1, 65432, $_iMaxClients)
 
    ; If an error occurred display the error code and return False.
    If @error Then
        Local $iError = @error
        MsgBox(BitOR($MB_SYSTEMMODAL, $MB_ICONHAND), "", "Could not listen, Error code: " & $iError)
        Return False
    EndIf
 
    Global $_sLogsDir = @ScriptDir & "\Logs"
    If FileExists($_sLogsDir) = 0 Then DirCreate($_sLogsDir)
 
    Global $_aClient[$_iMaxClients][6]
    Global Enum $_iClientSubIdx_Socket, $_iClientSubIdx_ComputerName, $_iClientSubIdx_FileHandle, $_iClientSubIdx_RecvBuffer, $_iClientSubIdx_Item, $_iClientSubIdx_LogLen
 
    Global $_iLastClientLog = -1
 
    Global Enum $_CM_DISCONNECT = 1000
 
    #region GUIMain
    AutoItSetOption("GUIOnEventMode", 1)
 
    Global $_hGUIMain = GUICreate("TCP server with multiple clients")
    GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
 
    Global $_iLwClient = GUICtrlCreateListView("Client|Date", 10, 10, 380, 380)
    Global $_hLwClient = GUICtrlGetHandle($_iLwClient)
 
    _GUICtrlListView_SetColumnWidth($_hLwClient, 0, 200)
    _GUICtrlListView_SetColumnWidth($_hLwClient, 1, 130)
 
    Global $_iDyContextMenu = GUICtrlCreateDummy()
    GUICtrlSetOnEvent($_iDyContextMenu, "_Dummy_ContextMenu")
 
    GUIRegisterMsg($WM_CONTEXTMENU, "WM_CONTEXTMENU")
    GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
    GUISetState(@SW_SHOW, $_hGUIMain)
    #endregion GUIMain
 
    #region GUILog
    Global $_hGUILog = GUICreate("", 600, 300, -1, -1, BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX), -1, $_hGUIMain)
    GUISetOnEvent($GUI_EVENT_CLOSE, "_GUILog_Hide")
 
    Global $_iEditLog = GUICtrlCreateEdit("", 0, 0, 600, 300, BitOR($WS_HSCROLL, $WS_VSCROLL, $ES_MULTILINE, $ES_READONLY, $ES_WANTRETURN))
    GUICtrlSetResizing($_iEditLog, $GUI_DOCKBORDERS)
 
    Global $_hEditLog = GUICtrlGetHandle($_iEditLog)
    #endregion GUILog
 
    Local $iSocket = 0, $vRecv = "", $sIPAddress = "", $aData = 0, $iEODPos = 0, $sLogRead = "", $iPos = 0, $sPacket = "", $fProcessBuffer = False
 
    While 1
        Sleep(10)
 
        ; Accept incomming connexions if present (Socket to close when finished; one socket per client).
        $iSocket = TCPAccept($iListenSocket)
 
        ; If an error occurred display the error code and exit the loop.
        If @error Then
            MsgBox(BitOR($MB_SYSTEMMODAL, $MB_ICONHAND), "", "Could not accept the incoming connection, Error code: " & @error)
            ExitLoop
        EndIf
 
        ; If a new client is connected.
        If $iSocket <> -1 Then
            For $i = 0 To $_iMaxClients - 1
                If($_aClient[$i][$_iClientSubIdx_Socket] <> "") Then ContinueLoop
 
                GUICtrlCreateListViewItem(SocketToIP($iSocket) & "|" & _Now(), $_iLwClient)
 
                $_aClient[$i][$_iClientSubIdx_Item] = _GUICtrlListView_GetItemCount($_hLwClient) - 1
 
                $_aClient[$i][$_iClientSubIdx_Socket] = $iSocket
                ExitLoop
            Next
        EndIf
 
        ; Loop through the connected clients.
        For $i = 0 To $_iMaxClients - 1
            If($_aClient[$i][$_iClientSubIdx_Socket] = "") Then ContinueLoop
 
            ; Receive from the client a maximum of 4 KiB data.
            $vRecv = TCPRecv($_aClient[$i][$_iClientSubIdx_Socket], 4096)
 
            ; If an error occurred disconnect the client and process the next one.
            If @error Then
                _Client_Disconnect($i)
                ContinueLoop
            EndIf
 
            $fProcessBuffer = False
 
            ; If nothing is received.
            If $vRecv = "" Then
                ; If the buffer is not empty, set the processbuffer variable to true.
                If $_aClient[$i][$_iClientSubIdx_RecvBuffer] <> "" Then
                    $fProcessBuffer = True
 
                    ; Move the client buffer to the receive variable.
                    $vRecv = $_aClient[$i][$_iClientSubIdx_RecvBuffer]
                    $_aClient[$i][$_iClientSubIdx_RecvBuffer] = ""
                Else
                    ; Process the next client.
                    ContinueLoop
                EndIf
            EndIf
 
            ; The TCPRecv function can return a string or a binary, if binary then convert it to string.
            If IsBinary($vRecv) = 1 Then $vRecv = BinaryToString($vRecv)
 
            ; If the data is not a new packet.
            If StringLeft($vRecv, 5) <> "{SOD}" Then
                ; If the client has not received a packet header.
                If $_aClient[$i][$_iClientSubIdx_RecvBuffer] = "" Then
                    ; If it's not a process buffer, disconnect the client and process the next one
                    If Not $fProcessBuffer Then
                        _Client_Disconnect($i)
                    EndIf
                    ContinueLoop
                EndIf
            EndIf
 
            ; Get the position of the packet footer.
            $iEODPos = StringInStr($vRecv, "{EOD}", 2) ;$STR_NOCASESENSEBASIC
 
            ; If the packet is incomplete, add it to the buffer and process the next client.
            If $iEODPos = 0 Then
                $_aClient[$i][$_iClientSubIdx_RecvBuffer] &= $vRecv
                ContinueLoop
            Else
                ; Assign the packet variable the end of the/full packet.
                $sPacket = $_aClient[$i][$_iClientSubIdx_RecvBuffer] & StringLeft($vRecv, $iEODPos - 1)
 
                ; Fill the buffer with the rest of the packet.
                $_aClient[$i][$_iClientSubIdx_RecvBuffer] = StringTrimLeft($vRecv, $iEODPos + 4) ;4 = {SOD} length -1
            EndIf
 
            ; The packet is valid, strip its header {SOD}.
            $sPacket = StringTrimLeft($sPacket, 5)
 
            ; If the client hasn't sent who he is.
            If($_aClient[$i][$_iClientSubIdx_ComputerName] = "") Then
                ; Retreive the IP Address of the client to add it to the log and replace it the whois info.
                $sIPAddress = _GUICtrlListView_GetItemText($_hLwClient, $i)
                _GUICtrlListView_SetItemText($_hLwClient, $i, $sPacket)
 
                ; Assign the whois info.
                $_aClient[$i][$_iClientSubIdx_ComputerName] = $sPacket
 
                ; Assign the log file handle.
                $_aClient[$i][$_iClientSubIdx_FileHandle] = FileOpen($_sLogsDir & "\" & _WindowsFileName($sPacket) & ".log", $FO_APPEND)
 
                ; Write in the log the connected infos.
                FileWriteLine($_aClient[$i][$_iClientSubIdx_FileHandle], @CRLF & "Client connected " & _StringRepeat("#", 64 - 17) & @CRLF & "IP Address: " & $sIPAddress & @CRLF)
            Else
                ; Write into the log the data received.
                FileWrite($_aClient[$i][$_iClientSubIdx_FileHandle], $sPacket)
            EndIf
 
            ; If the client is the client showed by the log GUI.
            If $i = $_iLastClientLog Then
                ; Note: The maximum data length of an edit is 32767, we will ensure that it never exceeds this limit.
 
                ; Increment the log length displayed with the data received.
                $_aClient[$i][$_iClientSubIdx_LogLen] += StringLen($sPacket)
 
                ; If the log length displayed is upper 30000.
                If $_aClient[$i][$_iClientSubIdx_LogLen] > 30000 Then
                    ; Trim the first 50 lines of the displayed log.
                    $sLogRead = GUICtrlRead($_iEditLog)
                    $iPos = StringInStr($sLogRead, @CRLF, 2, 50) + 1 ;$STR_NOCASESENSEBASIC
 
                    ; Substrarct the 50 lines length from the displayed length.
                    $_aClient[$i][$_iClientSubIdx_LogLen] -= $iPos
 
                    ; Substract the data and apply it to the edit.
                    GUICtrlSetData($_iEditLog, StringTrimLeft($sLogRead, $iPos))
                EndIf
 
                ; Append the data received.
                _GUICtrlEdit_AppendText(GUICtrlGetHandle($_iEditLog), $sPacket)
            EndIf
        Next
    WEnd
 
    ; Delete the GUI (and its child GUIs) and its controls.
    GUIDelete($_hGUIMain)
 
    $_iLastClientLog = -1
 
    ; Disconnect all clients.
    For $i = 0 To $_iMaxClients - 1
        If($_aClient[$i][$_iClientSubIdx_Socket] = "") Then ContinueLoop
        _Client_Disconnect($i)
    Next
 
    ; Close the Listening socket to allow afterward binds.
    ; While not closed, any other program can NOT bind to the same IP Address and Port.
    TCPCloseSocket($iListenSocket)
EndFunc   ;==>Example
 
Func _Client_Disconnect($iClient)
    ; If the client is the client showed by the log GUI hide the log.
    If $iClient = $_iLastClientLog Then
        _GUILog_Hide()
    EndIf
 
    ; If the client has a log file handle (meaning whois info received), write the disconnected info and close the file.
    If($_aClient[$iClient][$_iClientSubIdx_FileHandle] <> "") Then
        FileWriteLine($_aClient[$iClient][$_iClientSubIdx_FileHandle], @CRLF & @CRLF & "Client disconnected " & _StringRepeat("#", 64 - 20) & @CRLF)
        FileClose($_aClient[$iClient][$_iClientSubIdx_FileHandle])
    EndIf
 
    ; Close the client socket.
    TCPCloseSocket($_aClient[$iClient][$_iClientSubIdx_Socket])
 
    ; Delete the client listview item.
    _GUICtrlListView_DeleteItem($_hLwClient, $_aClient[$iClient][$_iClientSubIdx_Item])
 
    ; Decrement the listview item index of the above items.
    For $i2 = 0 To $_iMaxClients - 1
        If $_aClient[$i2][$_iClientSubIdx_Item] > $_aClient[$iClient][$_iClientSubIdx_Item] Then
            $_aClient[$i2][$_iClientSubIdx_Item] -= 1
        EndIf
    Next
 
    ; Clear the client resources.
    For $i2 = 0 To UBound($_aClient, 2) - 1
        $_aClient[$iClient][$i2] = ""
    Next
EndFunc   ;==>_Client_Disconnect
 
Func _GUILog_Hide()
    $_iLastClientLog = -1
    GUISetState(@SW_HIDE, $_hGUILog)
EndFunc   ;==>_GUILog_Hide
 
Func _Dummy_ContextMenu()
    Switch GUICtrlRead($_iDyContextMenu)
        Case $_CM_DISCONNECT
            Local $iSelItem = Number(_GUICtrlListView_GetSelectedIndices($_hLwClient))
            For $iClient = 0 To $_iMaxClients - 1
                If $_aClient[$iClient][$_iClientSubIdx_Item] = $iSelItem Then ExitLoop
            Next
            _Client_Disconnect($iClient)
    EndSwitch
EndFunc   ;==>_Dummy_ContextMenu
 
Func WM_CONTEXTMENU($hWnd, $Msg, $wParam, $lParam)
    #forceref $hWnd, $Msg, $wParam, $lParam
 
    If $hWnd <> $_hGUIMain Then Return $GUI_RUNDEFMSG
 
    Local $aCurInfo = GUIGetCursorInfo($hWnd)
    If $aCurInfo[4] <> $_iLwClient Then Return $GUI_RUNDEFMSG
 
    Local $aHit = _GUICtrlListView_HitTest($wParam)
    If $aHit[0] = -1 Then Return $GUI_RUNDEFMSG
 
    _GUICtrlListView_SetItemSelected($wParam, $aHit[0])
 
    Local $hMenu = 0, $iMenuID = 0
 
    $hMenu = _GUICtrlMenu_CreatePopup()
 
    _GUICtrlMenu_AddMenuItem($hMenu, "Disconnect", $_CM_DISCONNECT)
 
    $iMenuID = _GUICtrlMenu_TrackPopupMenu($hMenu, $wParam, -1, -1, 1, 1, 3)
    If $iMenuID Then GUICtrlSendToDummy($_iDyContextMenu, $iMenuID)
 
    _GUICtrlMenu_DestroyMenu($hMenu)
 
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_CONTEXTMENU
 
Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    #forceref $hWnd, $iMsg, $iwParam, $ilParam
    Local $iIDFrom = 0, $iCode = 0, $tNMHDR = 0
 
    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
 
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
 
    Local $tInfo = 0, $iItem = 0
 
    Switch $iIDFrom
        Case $_iLwClient
            Switch $iCode
                Case $NM_CLICK
                    $tInfo = DllStructCreate($tagNMLISTVIEW, $ilParam)
 
                    $iItem = DllStructGetData($tInfo, "Item")
                    If $iItem = -1 Then Return $GUI_RUNDEFMSG
 
                    WinSetTitle($_hGUILog, "", "Log - " & $_aClient[$iItem][$_iClientSubIdx_ComputerName])
                    GUISetState(@SW_SHOW, $_hGUILog)
 
                    GUICtrlSetData($_iEditLog, "")
                    _GUICtrlEdit_AppendText($_hEditLog, FileRead($_sLogsDir & "\" & $_aClient[$iItem][$_iClientSubIdx_ComputerName] & ".log"))
 
                    $_iLastClientLog = $iItem
            EndSwitch
    EndSwitch
 
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY
 
Func _WindowsFileName($sFileName)
    Return StringRegExpReplace($sFileName, '[\/:*?"<>|]', "")
EndFunc   ;==>_WindowsFileName
 
Func SocketToIP($iSocket)
    Local $tSockAddr = 0, $aRet = 0
    $tSockAddr = DllStructCreate("short;ushort;uint;char[8]")
    $aRet = DllCall("Ws2_32.dll", "int", "getpeername", "int", $iSocket, "ptr", DllStructGetPtr($tSockAddr), "int*", DllStructGetSize($tSockAddr))
    If Not @error And $aRet[0] = 0 Then
        $aRet = DllCall("Ws2_32.dll", "str", "inet_ntoa", "int", DllStructGetData($tSockAddr, 3))
        If Not @error Then Return $aRet[0]
    EndIf
    Return 0
EndFunc   ;==>SocketToIP
 
Func _Exit()
    Exit
EndFunc   ;==>_Exit
 
Func OnAutoItExit()
    TCPShutdown()
EndFunc   ;==>OnAutoItExit

_

Client: (taken from the TCPConnect helpfile example, I added three lines under the Added region)

#include <MsgBoxConstants.au3>
 
; I am the client, start me after the server! (Start first the TCPAccept example script).
 
Example()
 
Func Example()
    TCPStartup() ; Start the TCP service.
 
    ; Register OnAutoItExit to be called when the script is closed.
    OnAutoItExitRegister("OnAutoItExit")
 
    ; Assign Local variables the loopback IP Address and the Port.
    Local $sIPAddress = @IPAddress1 ; This IP Address only works for testing on your own computer.
    Local $iPort = 65432 ; Port used for the connection.
 
    ; Assign a Local variable the socket and connect to a Listening socket with the IP Address and Port specified.
    Local $iSocket = TCPConnect($sIPAddress, $iPort)
 
    ; If an error occurred display the error code and return False.
    If @error Then
        ; The server is probably offline/port is not opened on the server.
        Local $iError = @error
        MsgBox(BitOR($MB_SYSTEMMODAL, $MB_ICONHAND), "", "Could not connect, Error code: " & $iError)
        Return False
    EndIf
 
    #region Added
    ; Send the whois info.
    TCPSend($iSocket, "{SOD}I am " & @ComputerName & "{EOD}")
 
    ; Send some data (replace the header/footer code if present).
    TCPSend($iSocket, "{SOD}" & StringReplace(StringReplace(FileRead(@ScriptFullPath), "{SOD}", "[SOD]"), "{EOD}", "[EOD]") & "{EOD}")
 
    Sleep(5000)
    #endregion
 
    ; Close the socket.
    TCPCloseSocket($iSocket)
EndFunc   ;==>Example
 
Func OnAutoItExit()
    TCPShutdown() ; Close the TCP service.
EndFunc   ;==>OnAutoItExit

Br, FireFox.

Edited by FireFox
Link to comment
Share on other sites

:sweating: thank you for your complex code, I thought you made a simple one :sweating: . it is quite hard for me to understand all its features, because I am a beginner.

by the way I can't still understand what permits you to process an action while you're listening to other client connections.

is it the array or a background function?

ehi ehi ehi, what is your name?

Link to comment
Share on other sites

Yes it's "quite complex", that's why I added comments :)

by the way I can't still understand what permits you to process an action while you're listening to other client connections.

is it the array or a background function?

Array. Edited by FireFox
Link to comment
Share on other sites

I guess here the listening socket accepts a new connection and inserts it into the array:

; If a new client is connected.
        If $iSocket <> -1 Then
            For $i = 0 To $_iMaxClients - 1
                If($_aClient[$i][$_iClientSubIdx_Socket] <> "") Then ContinueLoop
 
                GUICtrlCreateListViewItem(SocketToIP($iSocket) & "|" & _Now(), $_iLwClient)
 
                $_aClient[$i][$_iClientSubIdx_Item] = _GUICtrlListView_GetItemCount($_hLwClient) - 1
 
                $_aClient[$i][$_iClientSubIdx_Socket] = $iSocket
                ExitLoop
            Next
        EndIf

next it loops through connected clients it finds in the array:

; Loop through the connected clients.
        For $i = 0 To $_iMaxClients - 1
            If($_aClient[$i][$_iClientSubIdx_Socket] = "") Then ContinueLoop
 
            ; Receive from the client a maximum of 4 KiB data.
            $vRecv = TCPRecv($_aClient[$i][$_iClientSubIdx_Socket], 4096)
 
            ; If an error occurred disconnect the client and process the next one.
            If @error Then
                _Client_Disconnect($i)
                ContinueLoop
            EndIf

now, I can't understand how can the code add a new user to array if it is processing the last loop.

ehi ehi ehi, what is your name?

Link to comment
Share on other sites

now, I can't understand how can the code add a new user to array if it is processing the last loop.

Huh?  :huh:

There is no multi threading, because it's not possible in autoit.

While 1
    ; Accept new client.
 
    ; If new client add it to the client array.
 
    ; Loop through the connected clients and process the packets.
 
    ; Go back to the start of the loop.
WEnd
Link to comment
Share on other sites

so, probably I understood. please wait. I'll try to search a bug into my code so multi clients will work also for me. :sorcerer:

alright!!! I checked it and I find what was wrong: you are right when you say it accepts many connections but it will manage them one by one.

I mean: when a client will connect I think it is put into an array but when it sends or receives message, it needs to be the only served from the server, so you need to create a three way handshake:

client request (tcpconnect), server answer (tcpsend) and flush start of client (tcprecv for answer and others for communication). :)

thank you so much

hear you soon :thumbsup:

Edited by binarydigit0101

ehi ehi ehi, what is your name?

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...