0

我在 AutoIt TCP 中度过了一段非常糟糕的时光。

我正在开发一个本地网络程序。我是服务器,4 台本地 PC 是客户端。我必须查看 4 台 PC 是否已打开,在线的必须列在 GUI 列表视图中。

我试过这个:ServerCl​​ient

它适用于 1 个客户,但如果我有超过 1 个客户,我无法让它工作。这个线程非常有用:www.autoitscript.com/forum/topic/144987-learning-about-tcp-servers-and-clients-connection/

但我仍然无法将它用于我的程序。

我必须使用数组,对吗?如何在 GUI 列表视图中显示数组?请你给我一个关于 MULTI CLIENT 的例子和一个查看在线客户端的列表。

4

1 回答 1

1

这是一个写得很好的多客户端 TCP 服务器。归功于肯·派珀。

#cs ----------------------------------------------------------------------------

AutoIt Version: 3.3.8.1
Author:      Ken Piper

Script Function:
    Template multi-client server base code.
    Use as a base for making an efficient server program.

    This base will just accept connections and echo back what it receives,
        and kill the connection if it is dead or inactive for x seconds.
    It will not do any other work, that must be added seperately!

#ce ----------------------------------------------------------------------------

TCPStartup()
Opt("TCPTimeout", 0)

#region ;Safe-to-edit things are below
Global $BindIP = "0.0.0.0"    ;Listen on all addresses
Global $BindPort = 8080        ;Listen on port 8080
Global $Timeout = 15000        ;Max idle time is 15 seconds before calling a connection "dead"
Global $PacketSize = 2048    ;Max packet size per-check is 2KB
Global $MaxClients = 50        ;Max simultaneous clients is 50
#endregion ;Stuff you shouldn't touch is below

Global $Listen
Global $Clients[1][4] ;[Index][Socket, IP, Timestamp, Buffer]
Global $Ws2_32 = DllOpen("Ws2_32.dll") ;Open Ws2_32.dll, it might get used a lot
Global $NTDLL = DllOpen("ntdll.dll") ;Open ntdll.dll, it WILL get used a lot
Global $CleanupTimer = TimerInit() ;This is used to time when things should be cleaned up

OnAutoItExitRegister("Close") ;Register this function to be called if the server needs to exit

$Clients[0][0] = 0
$Listen = TCPListen($BindIP, $BindPort, $MaxClients) ;Start listening on the given IP/port
If @error Then Exit 1 ;Exit with return code 1 if something was already bound to that IP and port

While 1
    USleep(5000, $NTDLL) ;This is needed because TCPTimeout is disabled. Without this it will run one core at ~100%.
    ;The USleep function takes MICROseconds, not milliseconds, so 1000 = 1ms delay.
    ;When working with this granularity, you have to take in to account the time it takes to complete USleep().
    ;1000us (1ms) is about as fast as this should be set. If you need more performance, set this from 5000 to 1000,
    ;but doing so will make it consume a bit more CPU time to get that extra bit of performance.
    Check() ;Check recv buffers and do things
    If TimerDiff($CleanupTimer) > 1000 Then ;If it has been more than 1000ms since Cleanup() was last called, call it now
        $CleanupTimer = TimerInit() ;Reset $CleanupTimer, so it is ready to be called again
        Cleanup() ;Clean up the dead connections
    EndIf
    Local $iSock = TCPAccept($Listen) ;See if anything wants to connect
    If $iSock = -1 Then ContinueLoop ;If nothing wants to connect, restart at the top of the loop
    Local $iSize = UBound($Clients, 1) ;Something wants to connect, so get the number of people currently connected here
    If $iSize - 1 > $MaxClients And $MaxClients > 0 Then ;If $MaxClients is greater than 0 (meaning if there is a max connection limit) then check if that has been reached
        TCPCloseSocket($iSock) ;It has been reached, close the new connection and continue back at the top of the loop
        ContinueLoop
    EndIf
    ReDim $Clients[$iSize + 1][4] ;There is room for a new connection, allocate space for it here
    $Clients[0][0] = $iSize ;Update the number of connected clients
    $Clients[$iSize][0] = $iSock ;Set the socket ID of the connection
    $Clients[$iSize][1] = SocketToIP($iSock, $Ws2_32) ;Set the IP Address the connection is from
    $Clients[$iSize][2] = TimerInit() ;Set the timestamp for the last known activity timer
    $Clients[$iSize][3] = "" ;Blank the recv buffer
WEnd

Func Check() ;Function for processing
    If $Clients[0][0] < 1 Then Return ;If there are no clients connected, stop the function right now
    For $i = 1 To $Clients[0][0] ;Loop through all connected clients
        $sRecv = TCPRecv($Clients[$i][0], $PacketSize) ;Read $PacketSize bytes from the current client's buffer
        If $sRecv <> "" Then $Clients[$i][3] &= $sRecv ;If there was more data sent from the client, add it to the buffer
        If $Clients[$i][3] = "" Then ContinueLoop ;If the buffer is empty, stop right here and check more clients
        $Clients[$i][2] = TimerInit() ;If it got this far, there is data to be parsed, so update the activity timer
        #region ;Example packet processing stuff here. This is handling for a simple "echo" server with per-packet handling
            $sRecv = StringLeft($Clients[$i][3], StringInStr($Clients[$i][3], @CRLF, 0, -1)) ;Pull all data to the left of the last @CRLF in the buffer
            ;This does NOT pull the first complete packet, this pulls ALL complete packets, leaving only potentially incomplete packets in the buffer
            If $sRecv = "" Then ContinueLoop ;Check if there were any complete "packets"
            $Clients[$i][3] = StringTrimLeft($Clients[$i][3], StringLen($sRecv) + 1) ;remove what was just read from the client's buffer
            $sPacket = StringSplit($sRecv, @CRLF, 1) ;Split all complete packets up in to an array, so it is easy to work with them
            For $j = 1 To $sPacket[0] ;Loop through each complete packet; This is where any packet processing should be done
                TCPSend($Clients[$i][0], "Echoing line: " & $sPacket[$j] & @CRLF) ;Echo back the packet the client sent
            Next
        #endregion ;Example
    Next
EndFunc

Func Cleanup() ;Clean up any disconnected clients to regain resources
    If $Clients[0][0] < 1 Then Return ;If no clients are connected then return
    Local $iNewSize = 0
    For $i = 1 To $Clients[0][0] ;Loop through all connected clients
        $Clients[$i][3] &= TCPRecv($Clients[$i][0], $PacketSize) ;Dump any data not-yet-seen in to their recv buffer
        If @error Or TimerDiff($Clients[$i][2]) > $Timeout Then ;Check to see if the connection has been inactive for a while or if there was an error
            TCPCloseSocket($Clients[$i][0]) ;If yes, close the connection
            $Clients[$i][0] = -1 ;Set the socket ID to an invalid socket
        Else
            $iNewSize += 1
        EndIf
    Next
    If $iNewSize < $Clients[0][0] Then ;If any dead connections were found, drop them from the client array and resize the array
        Local $iSize = UBound($Clients, 2) - 1
        Local $aTemp[$iNewSize + 1][$iSize + 1]
        Local $iCount = 1
        For $i = 1 To $Clients[0][0]
            If $Clients[$i][0] = -1 Then ContinueLoop
            For $j = 0 To $iSize
                $aTemp[$iCount][$j] = $Clients[$i][$j]
            Next
            $iCount += 1
        Next
        $aTemp[0][0] = $iNewSize
        $Clients = $aTemp
    EndIf
EndFunc

Func Close()
    DllClose($Ws2_32) ;Close the open handle to Ws2_32.dll
    DllClose($NTDLL) ;Close the open handle to ntdll.dll
    For $i = 1 To $Clients[0][0] ;Loop through the connected clients
        TCPCloseSocket($Clients[$i][0]) ;Force the client's connection closed
    Next
    TCPShutdown() ;Shut down networking stuff
EndFunc

Func SocketToIP($iSock, $hDLL = "Ws2_32.dll") ;A rewrite of that _SocketToIP function that has been floating around for ages
    Local $structName = DllStructCreate("short;ushort;uint;char[8]")
    Local $sRet = DllCall($hDLL, "int", "getpeername", "int", $iSock, "ptr", DllStructGetPtr($structName), "int*", DllStructGetSize($structName))
    If Not @error Then
        $sRet = DllCall($hDLL, "str", "inet_ntoa", "int", DllStructGetData($structName, 3))
        If Not @error Then Return $sRet[0]
    EndIf
    Return "0.0.0.0" ;Something went wrong, return an invalid IP
EndFunc

Func USleep($iUsec, $hDLL = "ntdll.dll") ;A rewrite of the _HighPrecisionSleep function made by monoceres (Thanks!)
    Local $hStruct = DllStructCreate("int64")
    DllStructSetData($hStruct, 1, -1 * ($iUsec * 10))
    DllCall($hDLL, "dword", "ZwDelayExecution", "int", 0, "ptr", DllStructGetPtr($hStruct))
EndFunc

现在关于数组到列表视图,

Dim $array[5]
$array[1] = "value 1"
$array[2] = "value 2"
$array[3] = "value 3"
$array[4] = "value 4"
$array[0] = UBound($array) - 1


For $i = 1 To $array[0]
    GUICtrlSetData($listview,$array[$i])
Next
于 2013-10-12T23:49:50.007 回答