Jump to content

Recommended Posts

Posted

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?

Posted
  On 8/22/2013 at 8:27 AM, FireFox said:

Let me make an example.

do you need an example?

  On 8/22/2013 at 9:26 AM, Pincopanco said:

Hi binarydigit0101

Also take a look >here

bye

alright, thank you. but where is the part about multi users?

ehi ehi ehi, what is your name?

Posted (edited)

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
Posted

: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?

Posted (edited)

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

  On 8/22/2013 at 11:32 AM, binarydigit0101 said:

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
Posted

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?

Posted
  On 8/22/2013 at 11:40 AM, binarydigit0101 said:

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
Posted

I believe the client is accepted internally then with the TCPAccept function you get the socket of the new client.

Posted (edited)

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?

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
  • Recently Browsing   0 members

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