Chinx7 Posted December 24, 2018 Share Posted December 24, 2018 Hello, I'm lurking on this forum for quite some time and it helped me a lot. But this time I couldn't find anything for my problem and neither could I on google. I am using autoit for a couple of years, but nothing serious, just some casual automation, so I would still consider myself a beginner. This time I tried to do a bigger "project" and butchered together some things i found on here and wrote some myself. My program consists of 2 scripts, a server and a client. The Server just handles the server stuff (duh) and some small calculations. The Client connects to a media player (currently only vlc) and communicates with it (only play/pause and current time of the video). It's nothing fancy, GUI and comments are still all over the place and more of a proof of concept. But now I hit a wall and I can't figure it out myself. When I connect the client to the server and try to send some data from the media player I get the error: ""C:\Users\chinx\Documents\Autoit\VideoSyncer\start vlc with webinterface.au3" (78) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.: Global $GlobalPlaystate = $playandtimeA[1] Global $GlobalPlaystate = ^ ERROR" On the Server I get the same in line 88 "$Timestamp = $PlayPauseAndTimestamp[1]" I tried to increase the Array size before using it. I get a console output with: "0 :0" and expected is "0:0" So I thought it could be a white space or a @CRLF causing the problem so I stripped it with StringStripWS. But I still get the same output. I tested some other things I can't remember and I just don't know how to proceed anymore, so I'm asking for help if anyone knows what the problem is. I hope the formatting is correct and I hope I don't have anything personal in the scripts, but here they are: Server: expandcollapse popup#cs ---------------------------------------------------------------------------- AutoIt Version: 3.3.8.1 (Or greater) 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 ---------------------------------------------------------------------------- #include <Array.au3> 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 Global $RangeTimestamp = 1000 ;Allowed Range of Timestamp +/- #endregion ;Stuff you shouldn't touch is below Global $Listen Global $Clients[1][6] ;[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(1000, $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][6] ;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 $Clients[$iSize][4] = "" ;Blank Play/Pause $Clients[$iSize][5] = "" ;Blank Timestamp 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 $PlayPauseAndTimestamp = StringSplit ($sPacket[$j],":",2) $PlayPause = $PlayPauseAndTimestamp[0] $Timestamp = $PlayPauseAndTimestamp[1] Next ;Update Status of [4]&[5] $Clients[$i][4] = $PlayPause $Clients[$i][5] = $Timestamp ;Compare Data with other Clients for $k = 1 to $Clients[0][0] If $Clients[$i][4] <> $Clients[$k][4] Then $discreptPP = 1 EndIf If $Clients[$k][5] > $Clients[$i][5]+$RangeTimestamp AND $Clients[$k][5] < $Clients[$i][5]-$RangeTimestamp Then $discreptTimestamp = 1 EndIf Next If $discreptPP Or $discreptTimestamp Then For $l = 1 to $Clients[0][0] TCPSend ($Clients[$l][0], $Clients[$i][4]&":"&$Clients[$i][5]&@CRLF) Next EndIf ;If needed send the updated data to Client ; How to Send Data: TCPSend($Clients[$i][0], "Echoing line: " & $sPacket[$j] & @CRLF) ;Echo back the packet the client sent #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 > 0 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 Func _ShowClientArray() _ArrayDisplay($Clients) EndFunc Client: expandcollapse popup#cs ---------------------------------------------------------------------------- AutoIt Version: 3.3.14.5 Author: myName Script Function: Template AutoIt script. #ce ---------------------------------------------------------------------------- ; Script Start - Add your code below here #include<File.au3> #include <GUIConstants.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <WindowsConstants.au3> #include <StringConstants.au3> Global $VLCplaying = 0 Global $VLCTimestamp = 0 Global $VLCPID Global $VLCconnected = 0 Global $Serverconnected = 0 Global $SocketServer = 0 Global $playandtimeA[2] OnAutoItExitRegister (_exit) $VLCPID = Run ('"C:\Program Files\VideoLAN\VLC\vlc.exe" --extraintf rc --rc-host localhost:2150') sleep (2000) TCPStartUp() $Form1 = GUICreate("Form1", 379, 108, 192, 124) $Input1 = GUICtrlCreateInput("127.0.2.1", 16, 72, 257, 21) $Button1 = GUICtrlCreateButton("Connect", 280, 72, 89, 25) $Label1 = GUICtrlCreateLabel("Not Connected", 16, 8, 36, 17) GUISetState(@SW_SHOW) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit Case $Button1 _ConnectToServer () _ConnectToPlayer () EndSwitch if $VLCconnected AND $Serverconnected Then _SendLocalState () _SyncLocalState () EndIf WEnd _VLCsendandlisten ("") _VLCsendandlisten ("") Func _SendLocalState () $timestamp = _VLCtimestamp () $playing = _VLCplaystate () TCPSend ($SocketServer,$playing & ":" & $timestamp & @CRLF) ConsoleWrite ($playing & ":" & $timestamp & @CRLF) EndFunc Func _GetGlobalState () $playandtimeA[0] = 0 $playandtimeA[1] = 0 $playandtime = TCPRecv ($SocketServer,2048) $playandtimeA = StringSplit ($playandtime,":",2) Global $GlobalTimestamp = $playandtimeA[0] Global $GlobalPlaystate = $playandtimeA[1] EndFunc Func _SyncLocalState () _GetGlobalState () if $GlobalPlaystate = 1 Then _VLCplay () ElseIf $GlobalPlaystate = 0 Then _VLCpause () EndIf _VLCsettimestamp ($GlobalTimestamp) EndFunc Func _exit () TCPShutdown () ProcessClose ($VLCPID) EndFunc Func _ConnectToPlayer () Global $SocketVLC=TCPConnect("127.0.0.1",2150) $VLCconnected = 1 If $SocketVLC=-1 Then MsgBox(0,"Error","Failed to connect to VLC over tcp proctol on localhost.Is it running and set to listen for console commands on port 2150?") $VLCconnected = 0 EndIf EndFunc Func _connectToServer () Global $SocketServer = TCPConnect (GUICtrlRead ($Input1),8080) $Serverconnected = 1 If $SocketServer=-1 Then MsgBox(0,"Error","Failed to connect to the Server, contact your master Chinx7 and giv moni.") $Serverconnected = 0 EndIf EndFunc Func _VLCtimestamp () _VLCsendandlisten ("get_time") EndFunc Func _VLCsettimestamp ($VLCTimestamp) _VLCsendandlisten ("seek " & $VLCTimestamp) EndFunc Func _VLCplay () if _VLCplaystate = 0 Then _VLCsendandlisten ("play") EndIf EndFunc Func _VLCpause () if _VLCplaystate = 1 Then _VLCsendandlisten ("play") EndIf EndFunc Func _VLCplaystate () Return _VLCsendandlisten ("is_playing") EndFunc func _VLCsendandlisten ($commandsend) TCPSend($SocketVLC,$commandsend&@CRLF) $listen = TCPRecv($SocketVLC,4096);Check if it gets someting back and shows it in inputbox Return $listen EndFunc Link to comment Share on other sites More sharing options...
pixelsearch Posted December 24, 2018 Share Posted December 24, 2018 (edited) Hi Chinx7 My guess is that, sometimes, your variable $playandtime doesn't contain any separator ":" Then the Array $playandtimeA is recreated with a single element [0], here : $playandtimeA = StringSplit ($playandtime,":",2) That's why you never get an error with $GlobalTimestamp = $playandtimeA[0] But the error arrives immediately after with $GlobalPlaystate = $playandtimeA[1] I suggest you add a test to detect that $playandtime contains a ":" each time the function is called, for example : $playandtime = TCPRecv ($SocketServer,2048) If StringInStr($playandtime, ":") = 0 Then MsgBox(0, "Error", "Separator : not found in string $playandtime") EndIf You'll tell us Edited December 24, 2018 by pixelsearch Chinx7 and FrancescoDiMuro 2 Link to comment Share on other sites More sharing options...
Chinx7 Posted December 24, 2018 Author Share Posted December 24, 2018 Ah, yeah that error message gets triggered, I need to sleep now, so i'll test that later and report back. Thank you though, haven't thought about that. Link to comment Share on other sites More sharing options...
Chinx7 Posted December 25, 2018 Author Share Posted December 25, 2018 Ok, I guess I need to look at some TCP-Tutorials, because I just don't get anything from the server, Is the way I'm trying to get information from the server by calling the TCPRecv in _GetGlobalState even correct or do I have to change that? But thanks to you I fixed another problem. I have to strip all whitespaces from what I'm sending and receiving, because the data I get is sometimes sprinkled with them. I'll try to find the problems and report back. 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