Das Wort "FTP" ist die Abkürzung für "File Transfer Protokoll" und bedeutet auf Deutsch so viel wie "Datei-Übertragsungs-Protokoll". Dieses Protokoll wird verwendet, um Dateien und Verzeichnisstrukturen von einem Rechner auf den anderen zu übertragen. Dabei ist festgelegt, dass ein Rechner der Empfänger (Client) und der andere Rechner der Sender (Server) ist. Eines der bekanntesten FTP-Programme ist ohne Zweifel "WS FTP" der Firma Ipswitch, Inc. Hierbei handelt es sich um einen FTP-Client. In unserem Workshop möchten wir Ihnen zeigen, wie ein solcher FTP-Client aufgebaut ist und vor allen Dingen, wie man einen eigenen FTP-Client programmieren kann. Arbeitsweise eines FTP-Clients Die Vorgehensweise eines FTP-Clients ist eigentlich ziemlich einfach. Zunächst wird versucht eine Verbindung zum Server herzustellen, wobei man eine bestimmte Reaktion (Antwort) zurückerhält. Diese erste Reaktion ist die Willkommensnachricht des Servers. Nach der Willkommensnachricht wird dem Server der Username mitgeteilt, sofern dieser es verlangt. Als Antwort erhält man vom Server, dass die Anmeldung (und somit der Benutzername) erfolgreich war, der Benutzername ungültig ist oder zusätzlich für die Anmeldung ein Passwort benötigt wird. Und genau nach diesem Schema erfolgt der gesamte Programmablauf. Es wird eine Anfrage gestartet und auf eine Antwort vom Server gewartet. Auf diese Antwort muss natürlich immer entsprechend reagiert werden. Das ganze wiederholt sich solange, bis beide "Parteien" (Server und Client) alle gewünschten Informationen erhalten haben. Für die Übertragung von Dateien und Verzeichnisstrukturen muss der Client einen neuen Port öffnen und diesen dem Server mitteilen. Der Server startet daraufhin eine Verbindung zu dem angegebenen Port und kann die gewünschte Datei senden (Download-Aktion) bzw. empfangen (Upload-Aktion). Der Port selbst wird auf Grundlage einer "Ort"-Berechnung erstellt, worauf wir aber später in diesem Workshop noch näher eingehen werden. Die Problematik Das einzige Problem, das sich bei diesem Konzept ergibt, ist, dass der Client nicht schneller arbeiten darf, als der Server die entsprechenden Antworten und Bestätigungen zurückschickt. Wird z.B. der Username an den Server übermittelt, muss unbedingt die Antwort des Server abgewartet werden - auch wenn bereits im Vorfeld bekannt ist, ob und welches Passwort vom Server verlangt wird. Hält man dieses Prozedere nicht ein, wird man sehr oft feststellen, dass an den Server übermittelte Kommandos von diesem nicht akzeptiert werden. Eine weitere Problematik, welche sich im Laufe des Workshop-Projekts herausgestellt ist, dass beim Abrufen oder Übertragen von Daten der Server bereits als Antwort "Übertragung beendet" gemeldet hat, obwohl das letzte Datenpaket noch gar nicht vollständig über den Datenport eingetroffen ist. Aus diesem Grund darf ein Datenport nicht sofort nach Erhalt der obigen Meldung geschlossen werden. Ohne dem WinSock-Control geht gar nichts! Für die Realisierung der FTP-Schnittstelle muss auf das WinSock-Control zurückgegriffen werden, da Visual Basic hierfür keine "hauseigenen" Befehle zur Verfügung stellt. Der Projekt selbst beruht auf einen komplexen Source-Code, so dass es nicht möglich ist, auf alle Funktionen und Prozeduren ausführlich einzugehen. In den nachfolgenden Kapiteln erfahren Sie, wie eine Form als Träger des Sourcecodes benutzt wird, um ein Objekt zu erstellen, das fast alle Funktionen eines modernen FTP-Clients unterstützt. Das WinSock-Control und seine (drei) Aufgaben: Die benötigten Deklarationen Fügen Sie nachfolgenden Code-Abschnitt in den Allgemein-Teil der Form ein. Es handelt sich hierbei um Konstanten-, Typen- und Variablen-Deklarationen. Die beiden API-Funktionen dienen lediglich dazu, einen Timeout festzulegen und das Clientfenster (die Form also) immer im Vordergrund zu halten. Anhand der Deklarationen ist bereits ersichtlich, über welche Funktionen der FTP-Client alles verfügen wird. Option Explicit ' API-Deklarationen Private Declare Function GetTickCount Lib "kernel32" () As Long Private Declare Function SetWindowPos Lib "user32.dll" ( _ ByVal hwnd As Long, _ ByVal hWndInsertAfter As Long, _ ByVal X As Long, _ ByVal Y As Long, _ ByVal cx As Long, _ ByVal cy As Long, _ ByVal wFlags As Long) As Long ' Server-Antworten ' ================ Private Enum ServerMessages ' Aufforderung für Benutzernamen ConnectMsg = 220 ' Benutzername ist OK UserNameOK = 331 ' Benutzername wurde nicht akzeptiert UserNameFail = 421 ' Passwort ist OK PasswordOK = 230 ' Passwort wurde nicht akzeptiert PasswordFail = 421 ' Passwort für den anonymen Zugang wurde nicht akzeptiert UserCantLogin = 530 ' Nachricht enthält das aktuelle Verzeichnis CurrentDirectory = 257 ' Kommando ist OK CommandOk = 200 ' Kommando wurde nicht ausgeführt CommandFail = 500 ' Datenübertragung wird gestartet TransferStart = 150 ' Datenübertragung beendet TransferComplete = 226 ' Verbindung zur Datenleitung ist fehlerhaft DataConnectionError = 425 ' Verbindung zur Datenleitung wurde geschlossen DataConnectionClosed = 426 ' Wechseln des Verzeichnisses war erfolgreich ChangeDirOK = 250 ' Server-Timeout, Verbindung wird geschlossen ConnectionTimeOut = 421 ' Dateigröße konnte ermittelt werden FileSizeOK = 213 ' Kommando ist nicht verfügbar CommandNotImplemented = 504 ' Zugriff verweigert PermissionDenied = 501 ' Fortsetzen möglich ResumingSupportet = 350 End Enum ' Client-Kommandos ' ================ ' Sendet den Benutzernamen Private Const User = "USER " ' Sendet das Passowort Private Const Password = "PASS " ' Beendet eine Sitzung Private Const QuitSession = "QUIT" ' Aufforderung für Benachrichtigung über das aktuelle ' Verzeichnis Private Const CurrentDir = "PWD" ' Aufforderung für alle Objekte im Verzeichnis über ' die Datenleitung Private Const ListFolder = "LIST" ' Daten sollen im Ascii-Format übermittelt werden Private Const OvermitAscii = "TYPE A" ' Setzt einen neuen Port für die Datenleitung Private Const SetPort = "PORT " ' Wechselt ein Verzeichnis Private Const ChangeFolder = "CWD " ' Wechselt in den übergeordneten Ordner Private Const ChangeFolderUP = "CDUP" ' Aufforderung zum Ermitteln einer Dateigröße Private Const FileSize = "SIZE " ' Fortsetzten eines UP-/Downloads Private Const ResumeTransfere = "REST " ' Daten sollen im Binär-Format übermittelt werden Private Const OvermitBinary = "TYPE I" ' Datei downloaden Private Const Download = "RETR " ' Up-/Download abbrechen Private Const AbortTransfere = "ABOR" ' Datei uploaden Private Const Upload = "STOR " ' Port Berechnungs-Konstanten ' =========================== Private Const PortStartValue = 8 Private Const PortValueStartValue = 80 ' TimeOut Konstanten ' ================== ' Maximale Zeit für einen Verbindungsversuch Private Const ConnectTimeOut = 30 ' Maximale Zeit für einen Kommando-Aufruf Private Const ReplyTimeOut = 30 ' Größe der Datenpakete für das Versenden Private Const SendBufferLenght = 1024 ' Globale Variablen zum Zwischenspeichern von Informationen ' ========================================================= ' Setzt ob das Fenster versteckt oder entladen werden soll ' (True = entladen) Dim DoClose As Boolean ' letzte übermittelte Wert des Servers Dim LastServerValue As String ' letzte übermittelte Nachricht des Servers Dim LastServerCmd As ServerMessages ' Puffer für das Listen eines Verzeichnisses Dim Data As String ' Größe der Download-Datei in Bytes Dim TotalBytes As Long ' bisher übertragene Anzahl Bytes Dim OvermittedBytes As Long ' Variable für eine freie Dateinummer Dim FFile As Long ' Wenn TRUE, wird die Dateiübertragung abgebrochen Dim TAbort As Boolean ' Datenpaket an den Server gesendet? (Upload) Dim PacketSend As Boolean Verbindung zum Server herstellen Bevor irgendeine Funktion, wie das Uploaden oder Downloaden einer Datei ausgeführt werden kann, muss natürlich erst einmal eine Verbindung zum FTP-Server hergestellt werden. Benötigt wird der Servername (RemoteHost) und der Port (Anschluss). Über die Connect-Methode wird der Verbindungsaufbau gestartet. Nun muss - ganz wichtig - zunächst geprüft werden, ob eine Verbindung zustande gekommen ist. Hierzu wird innerhalb einer "Warteschleife" die State-Eigenschaft abgefragt. Erhält man innerhalb der festgesetzten TimeOut-Zeit keine Antwort, wird die Schleife aufgrund einer Zeit-Überschreitung beendet. ' Verbindung zum Server herstellen Friend Function Connect(ByVal RemoteHost As String, _ ByVal Port As Long, ByVal UserName As String, _ ByVal Pass As String) As Boolean Dim TimeOut As Long ' Zeit-Limit (TimeOut) TimeOut = (GetTickCount / 1000) + ConnectTimeOut ' Verbinden With CmdSock .Close .RemoteHost = RemoteHost .RemotePort = Port .Connect End With ' Warten bis verbunden, oder Timeout eingetreten ist Do DoEvents If TimeOut < (GetTickCount / 1000) Then Exit Do Loop Until CmdSock.State >= sckConnected ' Besteht eine Verbindung? If CmdSock.State <> sckConnected Then ' Nein! Disconnect Exit Function End If Konnte eine Verbindung hergestellt werden, muss jetzt auf die Willkommensnachricht vom Server gewartet werden. ' Warten auf die Willkommensnachricht If SendCommand("", ConnectMsg, ConnectMsg, -1&, 0&, _ "Verbunden mit " & CmdSock.RemoteHostIP) = False Then ' Abgewiesen! Disconnect Exit Function End If Hat der Server sein OK gegeben, wird der Benutzername übermittelt. Der Benutzername wird daraufhin vom Server geprüft. Ist der Server mit dem Benutzername einverstanden, kann es aber sein, dass zum Endgültigen "Einloggen" noch zusätzlich ein Passwort benötigt wird. War die Antwort PasswordOK (230), so wird kein Passwort benötigt. Andernfalls muss jetzt vom Client das Benutzer-Passwort übermittelt werden. ' Benutzernamen senden und warten, ob Passwort benötigt wird If SendCommand(User & UserName, UserNameOK, PasswordOK, _ UserNameFail, 0&, "Anmelden") = False Then ' Abgewiesen! Ungültiger Benutzer Disconnect Exit Function End If ' Wird ein Passwort benötigt? If LastServerCmd = PasswordOK Then ' Kein Passwort notwendig! Connect = True Exit Function End If ' Passwort senden und auf Bestätigung der Verbindung warten If SendCommand(Password & Pass, PasswordOK, PasswordOK, _ UserCantLogin, 0&, "Erfolgreich angemeldet.") = False Then ' Abgewiesen! Ungültiges Passwort Disconnect Exit Function End If ' Erfolgreich verbunden! Connect = True End Function Immer wenn ein Verbindungsversuch mit dem Server gestartet wird, muss dem Server die eingehende Verbindung bestätigt bzw. akzeptiert werden. Das WinSock-Control löst hierfür das ConnectionRequest-Ereignis aus. ' Verbindungs-Bestätigung, wenn der Server eine Verbindung ' anfordert Private Sub CmdSock_ConnectionRequest(ByVal requestID As Long) CmdSock.Accept requestID End Sub In der Connect-Funktion wurde sehr häufig der Befehl SendCommand eingesetzt. Hierbei handelt es sich nicht um einen Standard-Befehl, sondern um eine Hilfsfunktion, welche Ihnen noch sehr häufig im Laufe unseres Workshops begegnen wird. Kommandos an den Server senden und auf Antwort warten Die Funktion SendCommand sendet ein bestimmtes Kommand an den Server (wie z.B. Username, Passwort oder Verzeichniswechsel) und wartet solange, bis der Server auf die "Anfrage" reagiert bzw. antwort. Als Funktionsparameter werden u.a. die gewünschten Antworten angegeben. Die Funktion gibt dann entweder True (wenn die gewünschte Antwort vom Server bestätigt wurde) oder False (wenn der Server das Kommando abgelehnt hat oder eine Zeitüberschreitung aufgetreten ist) zurück. ' Sendet ein Kommando an den Server und wartet auf Antwort Private Function SendCommand(ByVal CmdStr As String, _ ByVal WaitFor As ServerMessages, _ ByVal WaitFor2 As ServerMessages, _ ByVal WrongAbort As ServerMessages, _ Optional ByVal IgnoreValue As ServerMessages = 0, _ Optional ByVal StatusText As String = "") As Boolean Dim TimeOut As Long ' letzte Servernachricht zurücksetzen LastServerCmd = IgnoreValue ' TimeOut setzen TimeOut = (GetTickCount / 1000) + ReplyTimeOut ' Kommando an den Server senden If CmdStr <> "" Then CmdSock.SendData CmdStr & vbCrLf End If ' Warten bis die Serverantwort eintrifft ' oder ein TimeOut eintritt Do DoEvents If LastServerCmd = WrongAbort Then Exit Do If CmdSock.State <> sckConnected Then Exit Do If TimeOut < (GetTickCount / 1000) Then Exit Do Loop Until LastServerCmd <> IgnoreValue ' Wenn Verbindung unterbrochen wurde, dann alle Socks schliessen If CmdSock.State <> sckConnected Then Disconnect Exit Function End If ' Wenn Antowrt OK dann Funktion erfolgreich beenden If LastServerCmd = WaitFor Or LastServerCmd = WaitFor2 Then SendCommand = True End If End Function Ankommende Servernachrichten verarbeiten Der nachfolgende Code wird ausgeführt, wenn uns Servernachrichten erreichen. Die Nachricht speichern wir in zwei Variablen: LastServerCmd: enthält den dreistelligen Nachrichten-Wert Beide Variablen werden dann später in einer Funktion ausgewertet, um entsprechend auf die Servernachrichten reagieren bzw. diese auswerten zu können. ' Server-Nachrichten treffen ein Private Sub CmdSock_DataArrival(ByVal bytestotal As Long) Dim ServerCmd As String Dim TmpServerCmd As String ' Nachrichten-Daten ermitteln CmdSock.GetData ServerCmd TmpServerCmd = Left$(ServerCmd, 3) ' Das Kommando ist nur abgeschlossen, wenn nach dem Commado-ID ' ein Leerzeichen folgt If InStr(1, ServerCmd, TmpServerCmd & " ") <> 0 Then LastServerCmd = Left$(TmpServerCmd, 3) ' Server TimeOut If LastServerCmd = ConnectionTimeOut Then Disconnect Exit Sub End If LastServerValue = Replace(Mid$(ServerCmd, _ InStr(1, ServerCmd, TmpServerCmd & " ") + 4), vbCrLf, "") End If End Sub Ein paar Verzeichnisfunktionen vorweg Mit dem bisherigen Programmcode lässt sich nun schon einiges anfangen. Nachdem die Verbindung zum FTP-Server hergestellt wurde, kann man z.B. das aktuelle Verzeichnis ermitteln, in ein bestimmtes oder in einen übergeordneten Ordner wechseln. ' Aktuelles Verzeichnis auf dem Server ermitteln Friend Function GetCurrentDir() As String Dim sCurDir As String If SendCommand(CurrentDir, CurrentDirectory, CurrentDirectory, _ CommandFail) = False Then Exit Function End If ' Verzeichnisnamen sCurDir = Mid$(LastServerValue, 2) GetCurrentDir = Left$(sCurDir, InStr(1, sCurDir, """") - 1) End Function ' Verzeichnis wechseln Friend Function ChangeDir(ByVal DirName As String) As String If SendCommand(ChangeFolder & DirName, ChangeDirOK, _ ChangeDirOK, CommandFail) Then ' Im Erfolgsfall, aktuelles Verzeichnis zurückgeben ChangeDir = GetCurrentDir End If End Function ' In den übergeordneten Ordner wechseln Friend Function ChangeDirUp() As String If SendCommand(ChangeFolderUP, ChangeDirOK, ChangeDirOK, _ CommandFail) Then ' Im Erfolgsfall, aktuelles Verzeichnis zurückgeben ChangeDirUp = GetCurrentDir End If End Function Verzeichnisinformationen vom Server erfragen Aktuelles Verzeichnis ermitteln oder in ein anderes Verzeichnis wechseln - alles schön und gut, doch noch viel interessanter ist das Ermitteln der kompletten Verzeichnisstruktur (welche Verzeichnisse existieren und welche Dateien befinden sich in den Verzeichnissen). Für eine solche Anfrage an den Server verwenden wir ein eigenes Winsock-Control - in unserem Workshop DataSock genannt. Zunächst muss wir den lokalen Datenport auf Empfang stellen und dem Server diesen Port mitteilen, so dass er sich mit dem Datenport verbinden kann. Der Server erwartet hier einen String, der die IP-Adresse mit zwei folgenden Werten enthält, um den Port zu berechnen. Einen Port für die Datenübertragung berechnen Die nachfolgende Funktion erstellt einen eindeutigen Port und den dazugehörigen Port-String für den Server. ' Erstellen eines eindeutigen Ports und den dazugehörigen ' Portstring für den Server Private Function GetNewPort(ByRef Port As Long, _ ByRef PortStr As String) Static PortCount As Long Static PortValueCount As Long Dim TmpPort As Long Dim TmpPortValue As Long ' eindeutigen Port errechnen TmpPort = PortCount + PortStartValue TmpPortValue = PortValueCount + PortValueStartValue Port = TmpPort * 256 Or TmpPortValue ' PortString für den Server erstellen PortStr = Replace(CmdSock.LocalIP, ".", ",") & "," & TmpPort _ & "," & TmpPortValue ' Port bei jedem neuen Aufruf um eins erhöhen ' (darf jedoch 255 nicht übersteigen) If (PortValueCount + PortValueStartValue) + 1 >= 255 Then PortCount = PortCount + 1 PortValueCount = 0 Else PortValueCount = PortValueCount + 1 End If If (PortCount + PortStartValue) >= 255 Then PortCount = 0 End Function Verzeichnisinformationen in ein Array konvertieren Die vom Server übermittelten Verzeichnisinformationen sind relativ schwer auszulesen. Die einzelnen Verzeichnisse und Dateien sind hierbei durch eine vbCrLf-Zeichenfolge voneinander getrennt. Alle weiteren Detail-Informationen befinden sich quasi in einer Zeile und sind durch Leerzeichen voneinander getrennt. Das ganze stellt sich deshalb als schwierig heraus, da man nicht im vornherein sagen kann, wie viele Informationen bzw. wie viele Leerzeichen eine solche "Datenzeile" enthält. Und dann kann es noch vorkommen, dass der Dateiname selbst, sowie die Datumsangabe ebenfalls Leerzeichen enthalten. Knackpunkt ist also, die "Trenn-Leerzeichen" von den ggf. zusätzlich vorhandenen Leerzeichen zu unterscheiden. Und genau diese Aufgabe erledigt die nachfolgende Funktion. Die Funktion gibt dann das Ergebnis als Array zurück. ' Erstellt aus den Verzeichnisinformationen des Servers ein Array Private Function KonvertDirList(ByVal DirData As String) As Variant Dim AllFiles() As String Dim TmpArray() As String Dim i As Long Dim k As Long Dim TmpPos As Long ' Vorabinforamtionen des Servers entfernen If StrConv(Left$(DirData, 5), vbLowerCase) = "total" Then _ DirData = Mid$(DirData, InStr(1, DirData, vbCrLf) + 2) If DirData = "" Then Exit Function ' Array dimensionieren AllFiles = Split(DirData, vbCrLf) ReDim TmpArray(0 To UBound(AllFiles) - 1, 3) ' Für jede Datei die entsprechenden Informationen ermitteln For i = 0 To UBound(AllFiles) - 1 ' Attribute separieren TmpArray(i, 0) = Trim$(Left$(AllFiles(i), _ InStr(1, AllFiles(i), " ") - 1)) AllFiles(i) = Trim$(Replace(AllFiles(i), TmpArray(i, 0), "", 1, 1)) ' dazwischenliegene Leerzeichen ausgrenzen For k = 0 To 2 TmpPos = InStr(1, AllFiles(i), " ") AllFiles(i) = Trim$(Mid$(AllFiles(i), TmpPos)) Next k ' Dateigröße TmpArray(i, 1) = Trim$(Left$(AllFiles(i), _ InStr(1, AllFiles(i), " ") - 1)) AllFiles(i) = Trim$(Replace(AllFiles(i), TmpArray(i, 1), "", 1, 1)) ' Datum TmpArray(i, 2) = Left$(AllFiles(i), 12) AllFiles(i) = Trim$(Replace(AllFiles(i), TmpArray(i, 2), "", 1, 1)) ' Dateiname TmpArray(i, 3) = Trim$(AllFiles(i)) Next i ' Array zurückgeben KonvertDirList = TmpArray End Function Datenport öffnen und Verzeichnisinformationen ermitteln Die nachfolgende Funktion sendet eine Anfrage an den Server, mit der "Bitte" die Verzeichnisinformationen des aktuellen Ordners zurückzugeben. Zunächst muss der Übertragungsmode auf Ascii gesetzt werden. Danach wird ein neuer (Daten)-Port erstellt, der dann geöffnet wird. Den Port teilt man anschliessend dem Server mit, so dass dieser eine Verbindung zum Datenport herstellen kann. Über den LIST - Befehl wird dem Server mitgeteilt, dass man die aktuellen Verzeichnisinformationen ermitteln möchte. ' Inhalt eines Verzeichnisses als Array zurückgeben Friend Function GetDirList() As Variant Dim TmpPort As Long Dim TmpPortStr As String ' Servermode auf Ascii stellen If SendCommand(OvermitAscii, CommandOk, CommandOk, CommandFail, _ 0&, "Verzeichnissinhalt ermitteln") = False Then Exit Function End If NewUniquePort: ' Neuen Portstring erstellen Call GetNewPort(TmpPort, TmpPortStr) ' Datenleitung mit neuem Port öffnen DataSock.Close DataSock.LocalPort = TmpPort DataSock.Listen ' Den neu geöffneten Port dem Server mitteilen If SendCommand(SetPort & TmpPortStr, CommandOk, CommandOk, _ CommandFail, 0&, "Neuer Port geöffnet") = False Then Exit Function End If ' Dem Server mitteilen, dass wir die Verzeichnisinformationen ' benötigen Data = "" If SendCommand(ListFolder, TransferComplete, TransferComplete, _ DataConnectionClosed, TransferStart, _ "Verzeichnis übermittelt") = False Then GoTo NewUniquePort End If ' Ist noch ein Datenpäckchen unterwegs? Do DoEvents Loop Until DataSock.State <> 7 ' Sock schließen DataSock.Close ' Falls die Verbindung zur Datenleitung nicht aufgebaut werden ' konnte, nochmals versuchen If LastServerCmd = DataConnectionError Or _ LastServerCmd = DataConnectionClosed Then GoTo NewUniquePort GetDirList = KonvertDirList(Trim$(Data)) End Function Nachdem wir dem Sever den Datenport mitgeteilt haben, versucht der Server eine Verbindung aufzubauen. Hierzu wird das ConnectionRequest-Ereignis ausgelöst, in welchem wir den Server-Verbindungsaufbau "genehmigen" müssen. ' Server-Verbindungsaufbau "genehmigen" Private Sub DataSock_ConnectionRequest(ByVal requestID As Long) If DataSock.State = sckListening Then DataSock.Close Do DoEvents Loop Until DataSock.State = sckClosed DataSock.Accept requestID End If End Sub Die Verzeichnisinformationen selbst werden in einzelne "Datenpäckchen" gesendet. Immer wenn neue Daten eintreffen wird das DataArrival-Ereignis ausgelöst. Hier müssen wir die angekommenen Daten sammeln. Wir speichern diese also in eine Puffer-Variable. ' Ankommende Daten in den Puffer "Daten" kopieren Private Sub DataSock_DataArrival(ByVal bytestotal As Long) Dim TmpData As String If bytestotal = 0 Then Exit Sub ' Daten aus dem Pufferpool holen DataSock.GetData TmpData ' Jedes ankommende Datenpäckchen in unseren Puffer übertragen Data = Data & TmpData End Sub Ermitteln der Dateigröße einer Datei Das Ermitteln der Dateigröße einer Datei erfolgt in der Regel über das Kommando SIZE. Einige Server unterstützen diesen Befehl jedoch nicht. Dann muss die Dateigröße über die Verzeichnisinformationen ermittelt werden. ' Dateigröße ermitteln Friend Function GetFileSize(ByVal FileName As String) As Long Dim TmpFiles As Variant Dim i As Long ' Dateigröße über das SIZE-Kommando ermitteln If SendCommand(FileSize & FileName, FileSizeOK, FileSizeOK, _ CommandFail) = True Then GetFileSize = LastServerValue Exit Function End If ' Falls das SIZE-Kommando vom Server nicht unterstützt wird ' muss die Dateigröße über die Verzeichnisinformationen ' ermittelt werden Call GetDirList TmpFiles = KonvertDirList(Data) If IsArray(TmpFiles) Then For i = 0 To UBound(TmpFiles) If Mid$(FileName, InStrRev(FileName, "/") + 1) = TmpFiles(i, 3) Then GetFileSize = TmpFiles(i, 1) Exit Function End If Next i End If ' Fehler! GetFileSize = -1 End Function Beenden der Verbindung Beim Beenden der Verbindung zum Server muss das Kommando QuitSession gesendet werden. Danach kann die Close-Methode des CmdSock-Controls ausgeführt werden. Eine evtl. offene Datenleitung (Beim Beenden der Verbindung zum Server muss das Kommando QuitSession gesendet werden. Danach kann die Close-Methode des CmdSock-Controls ausgeführt werden. Eine evtl. offene Datenleitung (DataSock) sollte jetzt ebenfalls geschlossen werden.) sollte jetzt ebenfalls geschlossen werden. ' Verbindung trennen Friend Function Disconnect() Dim TmpIP As String ' Verbindung zum Server trennen TmpIP = CmdSock.RemoteHost If CmdSock.State = sckConnected Then CmdSock.SendData QuitSession CmdSock.Close End If ' Daten-Verbindung ebenfalls schliessen If DataSock.State = sckConnected Then DataSock.Close End If ' Anzeige aktualisieren ' Siehe Workshop-Projekt ' ... TAbort = True End Function Zusammenfassung Mit den bisher vorgestellten Befehlen und Funktionen lässt sich schon eine Art "Datei-Explorer" realisieren. Hierzu sind folgende Schritte durchzuführen
Hinweis zum Abschluss
Vorschau auf Teil 2 Im zweiten Teil unseres FTP-Workshops zeigen wir Ihnen, wie sich ein Up- und Download von Dateien realisieren lässt. Auch erfahren Sie, wie sich ein abgebrochener Up- oder Download fortsetzen lässt. Am Ende des zweiten Teils verfügen Sie somit über alle Informationen, um einen eigenen FTP-Client zu erstellen. Dieser Workshop wurde bereits 48.885 mal aufgerufen.
Anzeige
![]() ![]() ![]() (einschl. Beispielprojekt!) Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv! - nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten - Symbol-Galerie mit mehr als 3.200 Icons im modernen Look Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m. |
sevISDN 1.0 ![]() Überwachung aller eingehender Anrufe! Die DLL erkennt alle über die CAPI-Schnittstelle eingehenden Anrufe und teilt Ihnen sogar mit, aus welchem Ortsbereich der Anruf stammt. Weitere Highlights: Online-Rufident, Erkennung der Anrufbehandlung u.v.m. Tipp des Monats ![]() Manfred Bohn IndexOf für mehrdimensionale Arrays Die generische Funktion "IndexOf" ermittelt das erste Auftreten eines bestimmten Wertes in einem n-dimensionalen Array Neu! sevEingabe 3.0 ![]() Einfach stark! Ein einziges Eingabe-Control für alle benötigten Eingabetypen und -formate, inkl. Kalender-, Taschenrechner und Floskelfunktion, mehrspaltige ComboBox mit DB-Anbindung, ImageComboBox u.v.m. |
|||||||||||||||
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein. |