Hallo Zusammen,
ich bräuchte Eure Hilfe, da ich nicht weiter komme und vermutlich den Wald vor lauter Bäumen nicht sehe.
Sinn und Zweck meines Programmes ist es, von allen sich im Netz befindenden Geräten via UPnP (SSDP-Discover) die gesendeten Antworten 15 Sekunden lang zu sammeln. Alle 3 Sekunden sende ich erneut.
Mein Programm funktioniert unter Windows XP, egal wieviel Netzwerkkarten verbaut sind. Unter Windows 7 nur dann, wenn entweder nur eine Netzwerkkarte verbaut oder aktiv ist. D.h. wenn ich in Windows 7 alle Netzwerkkarten im Gerätemanger deaktiviere (auch die der VMware!), bis auf die eine, funktioniert auch dort das Programm. Sind alle Netzwerkkarten aktiv, funktioniert es nicht.
Jetzt stellt sich mir die Frage, ob ich einen Denk- und somit einen Programmfehler habe, oder ob das evtl. ein Bug in AutoIt ist. Ich hab dazu 2 Funktionen geschrieben.
Die SSDPdiscover_V1 funktioniert wie eben beschrieben. Da benutze ich für den UDPRecv den Port, der auch für das Senden benutzt wurde.
In SSDPdiscover_V2 habe ich verschiedene UDPBinds durchprobiert, ob es evtl. daran liegen könnte, kam aber nicht wirklich vorwärts. Evtl. mache ich da auch was falsch! Wenn ihr das testen wollt, müßt ihr den Aufruf auch in Main anpassen.
Hier mein Programm, ich hängt es als Attachment auch noch hin.
Spoiler anzeigen
#region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Outfile=.\SSDPdiscover.exe
#AutoIt3Wrapper_Compression=4
#AutoIt3Wrapper_Res_Description=
#AutoIt3Wrapper_Res_Fileversion=0.0.0.1
#AutoIt3Wrapper_Res_LegalCopyright=(c) by Rainer Ullrich
#AutoIt3Wrapper_Res_Language=1031
#AutoIt3Wrapper_Res_Field=Dateiname|SSDPdiscover
#AutoIt3Wrapper_Res_Field=Email|[email='rainer@rainerullrich.de'][/email]
#AutoIt3Wrapper_Run_After=del ".\SSDPdiscover*.au3.tbl"
#AutoIt3Wrapper_Run_After=del ".\SSDPdiscover*_Obfuscated.au3"
#AutoIt3Wrapper_Run_Obfuscator=y
#AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#Obfuscator_Parameters=/cs=1 /cn=1 /cf=1 /cv=1 /sf=1 /sv=1
#endregion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <Date.au3>
#include <Array.au3>
Opt("MustDeclareVars", 1)
[/autoit] [autoit][/autoit] [autoit]; --- MainVar-Deklaration ---
#region MainVar-Deklaration
; Für SSDPdiscover
Global $MyCollectedResponses ; Array mit den gesammelten Responses
Global $MyCollectedIPs ; Array mit den gesammelten IPs
Global $MyZeitZumSuchen = 15 * 1000 ; Zeit zum Suchen in Ticks
Global $MySendenIntervall = 3 * 1000; Alle x Ticks wird der ssdp-discover gesendet
Global $MyResultDatei = ".\ssdp-discover-result.txt"
Global $MyTemp, $MyError, $MyReturn
; MsgBox
Global Const $Mnormal = 0 + 262144
Global Const $Merror = 16 + 262144
Global Const $Mquestion = 32 + 262144
Global Const $Mattention = 48 + 262144
Global Const $Mresult = 64 + 262144
#endregion MainVar-Deklaration
; --- Main ---
#region Main
; -- SSDP Discxover via UPnP --
$MyReturn = SSDPdiscover_V1($MyCollectedResponses, $MyCollectedIPs, $MyZeitZumSuchen, $MySendenIntervall) ; hier ändern, also ..._V1 oder ..._V2 <-------------
$MyError = @error
If $MyError <> 0 Then
$MyTemp = "SSDPdiscover ist gescheitert!" & @CRLF & @CRLF
Switch $MyError
Case 0
; nix
Case 1
$MyTemp &= "UDPOpen-Fehler"
Case 2
$MyTemp &= "UDPSend-Fehler"
Case 3
$MyTemp &= "UDPRecv-Fehler"
Case 4
$MyTemp &= "UDPBind-Fehler"
EndSwitch
MsgBox($Merror, "Fehler:", $MyTemp)
Exit
EndIf
; -- Ergebnisausgabe --
; File löschen
If FileExists($MyResultDatei) Then
If Not FileDelete($MyResultDatei) Then
MsgBox($Merror, "Fehler:", 'Die Datei "' & $MyResultDatei & '" konnte nicht gelöscht werden!')
Exit
EndIf
EndIf
; Responses
If IsArray($MyCollectedResponses) Then
; _ArrayDisplay($MyCollectedResponses, "Gesammelte Responses")
$MyTemp = ''
For $e = 1 To $MyCollectedResponses[0]
$MyTemp &= $MyCollectedResponses[$e] & @CRLF
Next
If FileWrite($MyResultDatei, $MyTemp) Then
; Quickhack un die Datei zu öffnen
Run(@ComSpec & ' /c start "" "' & $MyResultDatei & '"', @WorkingDir, @SW_HIDE)
MsgBox($Mresult, "Ergebnis:", 'Das Ergebnis wurde in die Datei "' & $MyResultDatei & '" geschrieben!')
Else
MsgBox($Mresult, "Ergebnis:", 'Das Ergebnis konnte nicht in die Datei "' & $MyResultDatei & '" geschrieben werden!')
EndIf
Else
MsgBox(0, "Ergebnis:", "Es wurden via UPnP keine Responses geliefert!")
EndIf
; IP
If IsArray($MyCollectedIPs) Then
_ArrayDisplay($MyCollectedIPs, "Gesammelte IPs")
Else
MsgBox($Mresult, "Ergebnis:", "Es wurden via UPnP keine Boxen gefunden!")
EndIf
#endregion Main
; --- Funktionsdefinitionen ---
#region Funktionsdefinitionen
; Funktion, die via UPnP einen ssdp-discover durchführt und die Ergebnisse in ein Array schreibt.
; Außerdem werden die IP-Adressen der gefundenen Geräte in ein extra Array geschrieben
; V1: Receive-Socket = Send-Socket. Funktioniert unter XP immer und in Win 7 nur dann, wenn alle bis auf eine Netzwerkkarte im Gerätemanager deaktiviert ist
; Error:
; 0 = kein Error
; 1 = UDPOpen-Fehler
; 2 = UDPSend-Fehler
; 3 = UDPRecv-Fehler
; 4 = UDPBind-Fehler
Func SSDPdiscover_V1(ByRef $ResponsesArray, ByRef $IPArray, $TicksToSearch = 10000, $SendintervallInTicks = 1000)
; --- UPnP-Kommando ---
Local Const $UPnPcmd = _
'M-SEARCH * HTTP/1.1' & @CRLF & _
'ST:upnp:rootdevice' & @CRLF & _
'MX: 10' & @CRLF & _
'MAN: "ssdp:discover"' & @CRLF & _
'HOST: 239.255.255.250:1900' & @CRLF & _
@CRLF
Local $UPNPsendSocket, $UPNPreceiveSocket
Local $SendCounter = 0, $ReceiveCounter = 0
Local $ReceiveData, $NewIP
Local $StartTimeoutTimer, $UsedTimeoutTicks ; Timeout-Timer
Local $StartSendeTimer, $UsedSendeTicks ; Send-Timer
Local $OldRestSekunden = -99, $RestSekunden
Local $return = 1, $error = 0
; Arrays "löschen"
$ResponsesArray = ""
$IPArray = ""
; UPnP-Kommando ausgeben
ConsoleWrite(@CRLF)
ConsoleWrite("UPnP-Kommando:" & @CRLF)
ConsoleWrite($UPnPcmd & @CRLF)
; UDP starten
UDPStartup()
; - Sender -
; $array[1] contains the real socket, $array[2] contains the specified IP address and $array[3] contains the port
$UPNPsendSocket = UDPOpen("239.255.255.250", 1900)
ConsoleWrite("SendSocket: real socket: " & $UPNPsendSocket[1] & ", IP-address: " & $UPNPsendSocket[2] & ", port: " & $UPNPsendSocket[3] & @CRLF & @CRLF)
; _ArrayDisplay($UPNPsendSocket)
If $UPNPsendSocket[0] == -1 Or $UPNPsendSocket[0] == 0 Then ; Doku etwas schwammig
; Error 1
Return SetError(1, 0, 0)
EndIf
; - Empfänger -
; Socket muß der gleiche sein, sonst geht es nicht. Allerdings funktioniert es nicht auf Win 7-Rechnern mit mehreren Netzwerkkarten
$UPNPreceiveSocket = $UPNPsendSocket
; Timer setzen
$StartTimeoutTimer = TimerInit() ; Timeout-Timer
$StartSendeTimer = -99 ; Notlösung, kann man schöner programmieren
While 1
[/autoit] [autoit][/autoit] [autoit]; SenderPause berechnen
If $StartSendeTimer = -99 Then
; Timer wurde bisher noch nicht gesetzt, daher UsedTicks setzen
$UsedSendeTicks = $SendintervallInTicks + 10
Else
; Berechnen
$UsedSendeTicks = TimerDiff($StartSendeTimer)
EndIf
; Senden
If $UsedSendeTicks > $SendintervallInTicks Then
$SendCounter += 1
ConsoleWrite("UPnP Send Count Nr " & $SendCounter & @CRLF & @CRLF)
UDPSend($UPNPsendSocket, $UPnPcmd)
If @error <> 0 Then
$error = 2
$return = 0
ExitLoop
EndIf
; Timer rücksetzen
$StartSendeTimer = TimerInit()
EndIf
; kurze Pause (nach dem Senden)
Sleep(100)
; Empfangen
$ReceiveData = UDPRecv($UPNPreceiveSocket, 1024)
If @error <> 0 Then
$error = 3
$return = 0
ExitLoop
EndIf
If $ReceiveData <> "" Then
$ReceiveCounter += 1
ConsoleWrite("-------------------- Received Response " & $ReceiveCounter & ":" & " --------------------" & @CRLF)
ConsoleWrite($ReceiveData & @CRLF)
; Wenn neue Responds, dann hinzugügen
If AddItemToArray($ResponsesArray, $ReceiveData, 1) > 0 Then
ConsoleWrite("-> hinzugefügt" & @CRLF & @CRLF)
Else
ConsoleWrite("-> bereits vorhanden" & @CRLF & @CRLF)
EndIf
; IP extrahieren und sammeln
$NewIP = FetchIPfromUPNPdata($ReceiveData)
If $NewIP <> "" Then AddItemToArray($IPArray, $NewIP, 1)
EndIf
; Verbrauchte Zeit ermitteln
$UsedTimeoutTicks = TimerDiff($StartTimeoutTimer)
; Wenn die Zeit verstrichen ist, dann raus
If $UsedTimeoutTicks >= $TicksToSearch Then
ExitLoop
Else
; Restsekunden berechnen und als TrayTip ausgeben
$RestSekunden = Ceiling(($TicksToSearch - $UsedTimeoutTicks) / 1000)
If $RestSekunden <> $OldRestSekunden Then
TrayTip("SSPD-Discover", "Devices via UPnP suchen (noch " & $RestSekunden & " Sekunden)... ", 5, 1)
$OldRestSekunden = $RestSekunden
EndIf
EndIf
WEnd
; TrayTip schliessen
TrayTip("", "", 0)
; Socket schliessen
UDPCloseSocket($UPNPsendSocket)
UDPCloseSocket($UPNPreceiveSocket)
; UDP beenden
UDPShutdown()
; Sortieren
If IsArray($IPArray) Then _ArraySort($IPArray, 0, 1)
If IsArray($ResponsesArray) Then _ArraySort($ResponsesArray, 0, 1)
; Wenn Fehler vorhanden, dann Fehler zurückliefern
If $error > 0 Then
; Fehler zurückliefern
Return SetError($error, 0, $return)
Else
; Normaler Return
Return $return
EndIf
EndFunc ;==>SSDPdiscover_V1
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]; Funktion, die via UPnP einen ssdp-discover durchführt und die Ergebnisse in ein Array schreibt.
; Außerdem werden die IP-Adressen der gefundenen Geräte in ein extra Array geschrieben
; V2: Receive-Socket und Send-Socket sind unterschiedlich. Ich hab mal verschiedene Dinge durchprobiert, keine funktioniert.
; Error:
; 0 = kein Error
; 1 = UDPOpen-Fehler
; 2 = UDPSend-Fehler
; 3 = UDPRecv-Fehler
; 4 = UDPBind-Fehler
Func SSDPdiscover_V2(ByRef $ResponsesArray, ByRef $IPArray, $TicksToSearch = 10000, $SendintervallInTicks = 1000)
; --- UPnP-Kommando ---
Local Const $UPnPcmd = _
'M-SEARCH * HTTP/1.1' & @CRLF & _
'ST:upnp:rootdevice' & @CRLF & _
'MX: 10' & @CRLF & _
'MAN: "ssdp:discover"' & @CRLF & _
'HOST: 239.255.255.250:1900' & @CRLF & _
@CRLF
Local $UPNPsendSocket, $UPNPreceiveSocket
Local $SendCounter = 0, $ReceiveCounter = 0
Local $ReceiveData, $NewIP
Local $StartTimeoutTimer, $UsedTimeoutTicks ; Timeout-Timer
Local $StartSendeTimer, $UsedSendeTicks ; Send-Timer
Local $OldRestSekunden = -99, $RestSekunden
Local $return = 1, $error = 0
; Arrays "löschen"
$ResponsesArray = ""
$IPArray = ""
; UPnP-Kommando ausgeben
ConsoleWrite(@CRLF)
ConsoleWrite("UPnP-Kommando:" & @CRLF)
ConsoleWrite($UPnPcmd & @CRLF)
; UDP starten
UDPStartup()
; - Sender -
; $array[1] contains the real socket, $array[2] contains the specified IP address and $array[3] contains the port
$UPNPsendSocket = UDPOpen("239.255.255.250", 1900)
ConsoleWrite("SendSocket: real socket: " & $UPNPsendSocket[1] & ", IP-address: " & $UPNPsendSocket[2] & ", port: " & $UPNPsendSocket[3] & @CRLF & @CRLF)
; _ArrayDisplay($UPNPsendSocket)
If $UPNPsendSocket[0] == -1 Or $UPNPsendSocket[0] == 0 Then ; Doku etwas schwammig
; Error 1
Return SetError(1, 0, 0)
EndIf
; - Empfänger -
If @IPAddress1 <> "0.0.0.0" Then ConsoleWrite(@IPAddress1 & @CRLF)
If @IPAddress2 <> "0.0.0.0" Then ConsoleWrite(@IPAddress2 & @CRLF)
If @IPAddress3 <> "0.0.0.0" Then ConsoleWrite(@IPAddress3 & @CRLF)
If @IPAddress4 <> "0.0.0.0" Then ConsoleWrite(@IPAddress4 & @CRLF)
; $UPNPreceiveSocket = UDPBind("127.0.0.1", 1900)
; $UPNPreceiveSocket = UDPBind(@IPAddress1, 1900)
; $UPNPreceiveSocket = UDPBind("127.0.0.1", $UPNPsendSocket[1])
; $UPNPreceiveSocket = UDPBind(@IPAddress1, $UPNPsendSocket[1])
; $UPNPreceiveSocket = UDPBind("239.255.255.250", 1900)
$UPNPreceiveSocket = UDPBind(@IPAddress1, 1900)
ConsoleWrite("ReceiveSocket: real socket: " & $UPNPreceiveSocket[1] & ", IP-address: " & $UPNPreceiveSocket[2] & ", port: " & $UPNPreceiveSocket[3] & @CRLF & @CRLF)
; _ArrayDisplay($UPNPsendSocket)
If $UPNPreceiveSocket[0] == -1 Or $UPNPreceiveSocket[0] == -0 Then ; Doku etwas schwammig
; Error 4
Return SetError(4, 0, 0)
EndIf
; Timer setzen
$StartTimeoutTimer = TimerInit() ; Timeout-Timer
$StartSendeTimer = -99 ; Notlösung, kann man schöner programmieren
While 1
[/autoit] [autoit][/autoit] [autoit]; SenderPause berechnen
If $StartSendeTimer = -99 Then
; Timer wurde bisher noch nicht gesetzt, daher UsedTicks setzen
$UsedSendeTicks = $SendintervallInTicks + 10
Else
; Berechnen
$UsedSendeTicks = TimerDiff($StartSendeTimer)
EndIf
; Senden
If $UsedSendeTicks > $SendintervallInTicks Then
$SendCounter += 1
ConsoleWrite("UPnP Send Count Nr " & $SendCounter & @CRLF & @CRLF)
UDPSend($UPNPsendSocket, $UPnPcmd)
If @error <> 0 Then
$error = 2
$return = 0
ExitLoop
EndIf
; Timer rücksetzen
$StartSendeTimer = TimerInit()
EndIf
; kurze Pause (nach dem Senden)
Sleep(100)
; Empfangen
$ReceiveData = UDPRecv($UPNPreceiveSocket, 1024)
If @error <> 0 Then
$error = 3
$return = 0
ExitLoop
EndIf
If $ReceiveData <> "" Then
$ReceiveCounter += 1
ConsoleWrite("-------------------- Received Response " & $ReceiveCounter & ":" & " --------------------" & @CRLF)
ConsoleWrite($ReceiveData & @CRLF)
; Wenn neue Responds, dann hinzugügen
If AddItemToArray($ResponsesArray, $ReceiveData, 1) > 0 Then
ConsoleWrite("-> hinzugefügt" & @CRLF & @CRLF)
Else
ConsoleWrite("-> bereits vorhanden" & @CRLF & @CRLF)
EndIf
; IP extrahieren und sammeln
$NewIP = FetchIPfromUPNPdata($ReceiveData)
If $NewIP <> "" Then AddItemToArray($IPArray, $NewIP, 1)
EndIf
; Verbrauchte Zeit ermitteln
$UsedTimeoutTicks = TimerDiff($StartTimeoutTimer)
; Wenn die Zeit verstrichen ist, dann raus
If $UsedTimeoutTicks >= $TicksToSearch Then
ExitLoop
Else
; Restsekunden berechnen und als TrayTip ausgeben
$RestSekunden = Ceiling(($TicksToSearch - $UsedTimeoutTicks) / 1000)
If $RestSekunden <> $OldRestSekunden Then
TrayTip("SSPD-Discover", "Devices via UPnP suchen (noch " & $RestSekunden & " Sekunden)... ", 5, 1)
$OldRestSekunden = $RestSekunden
EndIf
EndIf
WEnd
; TrayTip schliessen
TrayTip("", "", 0)
; Socket schliessen
UDPCloseSocket($UPNPsendSocket)
UDPCloseSocket($UPNPreceiveSocket)
; UDP beenden
UDPShutdown()
; Sortieren
If IsArray($IPArray) Then _ArraySort($IPArray, 0, 1)
If IsArray($ResponsesArray) Then _ArraySort($ResponsesArray, 0, 1)
; Wenn Fehler vorhanden, dann Fehler zurückliefern
If $error > 0 Then
; Fehler zurückliefern
Return SetError($error, 0, $return)
Else
; Normaler Return
Return $return
EndIf
EndFunc ;==>SSDPdiscover_V2
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]; Funktion, die aus den ReceivedPacket die IP-Adresse extrahiert
Func FetchIPfromUPNPdata($Data)
#cs Beispiel:
HTTP/1.1 200 OK
LOCATION: http://192.168.5.1:49000/igddesc.xml
SERVER: WLAN_VDSL_Ullrich UPnP/1.0 AVM FRITZ!Box Fon WLAN 7270 54.04.74
CACHE-CONTROL: max-age=1800
EXT:
ST: upnp:rootdevice
USN: uuid:75802409-bccb-40e7-8e6c-001F3F56E239::upnp:rootdevice
#ce
; --- Einfach mit RegExp die IP rausfiltern ---
; Local $IP = StringRegExp($Data, "LOCATION: http://\d+\.\d+\.\d+\.\d+:49", 1)
Local $IP = StringRegExp($Data, "\d+\.\d+\.\d+\.\d+", 1)
; _ArrayDisplay($IP)
; Scheun ob es ein Array ist, wenn nicht raus
If Not IsArray($IP) Then Return ""
; Ich bauche erstes Element aus dem RegExp-Ergebnis
$IP = $IP[0]
; Überprüfen auf Gültigkeit
If Not _IsIPv4($IP) Then Return ""
Return $IP
[/autoit] [autoit][/autoit] [autoit]EndFunc ;==>FetchIPfromUPNPdata
[/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]; Funktion, die überprüft, ob es eine gültige IP4-IP ist
Func _IsIPv4($S_IP)
If StringRegExp($S_IP, "\A(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])\z") Then Return (1)
Return (0)
EndFunc ;==>_IsIPv4
; Funktion, die ein Item an das Array (mit Zelle) hinzufügt und dabei den Counter in Zelle 0 um eins erhöht
; Ist das Array noch leer, wird es angelegt. Es wird der Counter zurückgeliefert
; Wenn $OnlyIfNew = 1, dann wird vorher geschaut, ob es schon im Array vorhanden ist
Func AddItemToArray(ByRef $ArrayMitZelleNull, $value, $OnlyIfNew = 0) ; Fügt zum Array ein Item hinzu und erhöht den Wert in Zelle 0. Ist das Array leer, wird eines erzeugt
; Schauen, ob es ein Array ist. Wenn nicht, dann wird es angelegt und der Wert hinzugefügt
If IsArray($ArrayMitZelleNull) Then
; es ist ein Array
; ggf. schauen, ob es bereits enthalten ist
If $OnlyIfNew == 1 Then
; Raus, wenn es bereist enthalten ist, also größer als 0
If _ArraySearch($ArrayMitZelleNull, $value, 1) > 0 Then
Return 0
EndIf
EndIf
; Element hinzufügen
Local $ret = _ArrayAdd($ArrayMitZelleNull, $value)
; wenn ungleich -1 dann den Counter erhöhen
If $ret <> -1 Then
; um 1 erhöhren in Zelle 0
Local $Count = $ArrayMitZelleNull[0] + 1
$ArrayMitZelleNull[0] = $Count
; Index, also Count zurückliefern
Return $Count
Else
Return -1
EndIf
Else
; Es ist kein Array, daher erzeugen und Wert hinzufügen
Dim $ArrayMitZelleNull[2]
$ArrayMitZelleNull[0] = 1
$ArrayMitZelleNull[1] = $value
Return 1
EndIf
EndFunc ;==>AddItemToArray
#endregion Funktionsdefinitionen
Ja, ich weiß, es gibt auch eine Methode via Obj, aber die liefert mir nicht die von mir gesuchten "Locations".
Hat jemand eine Idee, was ich falsch mache?
Besten Dank und viele Grüße
R@iner