Chinx7 Posted December 24, 2018 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
pixelsearch Posted December 24, 2018 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 FrancescoDiMuro and Chinx7 2 "I think you are searching a bug where there is no bug... don't listen to bad advice."
Chinx7 Posted December 24, 2018 Author 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.
Chinx7 Posted December 25, 2018 Author 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.
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