Dieser Artikel wurde maschinell übersetzt.

Windows Phone

Hinzufügen von FTP-Unterstützung in Windows Phone 8

Uday Gupta

FTP ist eines der am häufigsten verwendeten Protokolle für den Austausch von Dateien.Es dient zur hand Dateien an Clients, um Installationsdateien für die Verbraucher Zugang zu sonst unzugängliche Teile des Dateisystems aus Sicherheitsgründen in Enterprise-Umgebungen und für eine Vielzahl von anderen Szenarien zu verteilen.Mit dem Schwerpunkt auf der Wolke scheint intuitiv heute es weiterhin auf FTP zu gründen.Aber wenn Sie die direkten Kontrolle über Ihre Dateien und bietet einfachen Zugang zu ihnen haben möchten, FTP kann immer noch der Weg zu gehen.

Der Zusatz von Socket-Unterstützung in Windows Phone-fähige Geräte für die Kommunikation mit einer Vielzahl von verschiedenen Dienstleistungen, so dass Benutzer mehr verbunden und zur Verfügung als je zuvor fühlen.Jedoch kann es in Windows Phone (sowie Windows 8), bis zu dem Entwickler, seine eigene Implementierung eines Dienstes bereit sein.Ein Feature, das Windows Phone noch fehlt ist die Unterstützung für FTP — es gibt keine direkte APIs zur Nutzung von FTP-Diensten in einer Windows-Speicher-app.Aus Sicht der Unternehmen ist es ziemlich schwierig für Mitarbeiter Zugriff auf Dateien über ihre Handys.

Dieser Artikel ist über die Unterstützung für FTP auf Windows Phone 8 durch die Schaffung einer FTP-Bibliothek und eine einfache FTP-Clientanwendung, die auf einem Windows Phone-Gerät ausgeführt werden.FTP ist ein etablierter, gut dokumentierten Protokoll (siehe RFC 959 bei bit.ly/fB5ezA).RFC 959 beschreibt die Spezifikation, seine Funktionalität und Architektur und seine Befehle mit ihren Antworten.Ich werde nicht jedes Feature und jeder Befehl FTP zu beschreiben, aber ich hoffe auf einen Vorsprung zum Erstellen Ihrer eigenen FTP-Implementierung bereitstellen.Beachten Sie, dass die Funktion werde ich diskutieren kein Microsoft-Implementierung ist und nicht unbedingt genehmigen kann.

FTP-Kanäle

FTP besteht aus zwei Kanälen, ein Befehlskanal und ein Datenkanal.Der Befehlskanal wird erstellt, wenn ein Client eine Verbindung, auf einen ftpserver herstellt, und es verwendet wird, um alle Befehle und Antworten an und von der FTP-Server übertragen.Der Befehlskanal bleibt geöffnet, bis der Client die Verbindung trennt, ein Verbindung im Leerlauf-Timeout erfolgt oder ein Fehler, in den FTP-Server oder Befehl-Kanal auftritt.

Datenkanal wird verwendet, um Daten zwischen dem FTP-Server übertragen.Dieser Kanal ist vorübergehender Natur — sobald die Datenübertragung abgeschlossen ist, wird der Kanal getrennt.Für jede Datenübertragung ist ein neue Datenkanal eingerichtet.

Eine FTP-Verbindung kann aktiv oder passiv sein.Im aktiven Modus sendet der FTP-Client die Kanalinformationen für die Daten (die Daten-Port-Nummer) an die die Übertragung gesendet werden.Im passiven Modus fragt der Client den Server erstellen einen Datenkanal an ihrem Ende und die Socket-Adresse und Port-Informationen bieten, so dass der Client verbinden Sie mit diesen Datenkanal und den Datei-Transfer-Vorgang beginnen kann.

Passiver Modus ist nützlich, wenn der FTP-Client nicht haben, Datenanschlüsse oder Datenkanäle auf seinem Ende zu verwalten will.Bei der Entwicklung des FTP-Clients für Windows Phone, werde ich in den passiven Modus wechseln.Das heißt, bitten ich vor Beginn einer Dateiübertragung-Operation, den FTP-Server erstellen und öffnen einen Datenkanal und senden Sie mir ihre Socket Endpunktinformationen, so dass wenn ich eine Verbindung herstellen, die Datenübertragung beginnt.Erörtert passiven Modus später in diesem Artikel veranschaulicht.

Erste Schritte

In diesem Artikel werden nicht alle Befehle, die in RFC 959 erwähnt behandelt.Ich werde stattdessen konzentrieren, auf die grundlegenden Befehle, die eine minimale, funktionsfähiges FTP-Client, einschließlich der Beteiligten im Verbindung erstellen müssen-Verfahren, Authentifizierung, das Dateisystem durchsuchen und hochladen und Herunterladen von Dateien zu trennen.

Um loszulegen, werde ich eine Visual Studio -Projektmappe erstellen, die zwei Projekte, ein Windows Phone-Klassenbibliothek-Projekt für die FTP-Bibliothek und ein Windows Phone App-Projekt Code für das UX und zur Benutzung der FTP-Bibliothek enthalten enthalten wird.

Um die Lösung zu erstellen, öffnen Sie Visual Studio, erstellen Sie ein Windows Phone-Klassenbibliothek-Projekt und nennen Sie es WinPhoneFtp.FtpService.

Jetzt fügen Sie ein Windows Phone App-Projekt und nennen Sie es WinPhone­-Ftp.UserExperience.Dies wird con­Tain der Benutzeroberfläche für die Test-app.

WinPhoneFtp.FtpService ist der FTP-Client-Bibliothek, die in das WinPhoneFtp.UserExperience UX-Projekt verwiesen wird, um FTP-Dienste zu nutzen.Die FTP-Bibliothek nutzt eine ereignisbasierte, Async/erwarten-Muster.Jede Operation wird asynchron in einem Hintergrundtask aufgerufen werden, und nach der Fertigstellung ein Ereignis wird ausgelöst um Benachrichtigung in der Benutzeroberfläche bereitzustellen.Die FTP-Client-Bibliothek enthält verschiedene asynchrone Methoden für jeden FTP-Vorgang (unterstützten Befehle) und ein bestimmtes Ereignis, das ausgelöst wird, wenn die asynchrone Methode schließt jede asynchrone Methode zugeordnet ist.Abbildung 1 zeigt die Zuordnung von asynchronen Methoden und Ereignisse.

Abbildung 1 asynchrone Methoden und zugeordneten Ereignisse

Methode (mit Parametern) Zugeordneten Ereignisse
Konstruktor (System.String-IP-Adresse, System.Windows.Threading.Dispatcher-UIDispatcher) Keine zugeordnete Ereignis
ConnectAsync FtpConnected
DisconnectAsync FtpDisconnected

AuthenticateAsync

AuthenticateAsync (System.String Username, System.String-Passwort)

Erfolg - FtpAuthenticationSucceeded

Fehler - FtpAuthenticationFailed

GetPresentWorkingDirectoryAsync FtpDirectoryListed
ChangeWorkingDirectoryAsync

Erfolg - FtpDirectoryChangedSucceeded

Fehler - FtpDirectoryChangedFailed

GetDirectoryListingAsync FtpDirectoryListed
UploadFileAsync (System.IO.Stream LocalFileStream, System.String Remotedateiname)

Erfolg - FtpFileUploadSucceeded

Fehler - FtpFileUploadFailed

Fortschritt - FtpFileTransferProgressed

DownloadFileAsync (System.IO.Stream LocalFileStream, System.String Remotedateiname)

Erfolg - FtpFileDownloadSucceeded

Fehler - FtpFileDownloadFailed

Fortschritt - FtpFileTransferProgressed

Benutzeroberfläches der Anwendung besteht aus zwei Textfelder und zwei Schaltflächen.Eine Textbox und Button werden verwendet für die Entgegennahme der FTP-Server-IP-Adresse und anschließen.Die anderen Textbox und Button werden FTP-Befehle von einem Benutzer zu akzeptieren und senden sie an den FTP-Server.

Der TCP-Socket-Wrapper

Vor dem Start auf dem FTP-Client, ich habe einen Wrapper mit dem Namen TcpClientSocket für die Windows.Networking.Sockets.StreamSocket-Klasse (siehe bit.ly/15fmqhK).Dieser Wrapper bieten bestimmte Ereignisbenachrichtigungen basierend auf verschiedenen Socketoperationen.Die Benachrichtigungen, die mich interessieren sind SocketConnected, DataReceived, ErrorOccured und SocketClosed.Wenn das Objekt StreamSocket zum Remoteendpunkt verbindet, wird das SocketConnected-Ereignis ausgelöst.In ähnlicher Weise auf empfangen von Daten über die Buchse, die Daten­Received-Ereignis wird ausgelöst, zusammen mit den Puffer mit Daten als Ereignisargumente.Wenn beim Socket-Vorgang ein Fehler auftritt, ist es das ErrorOccured-Ereignis, das die Benachrichtigung hat.Und schließlich, wenn die Buchse (durch lokale oder entfernte Host) geschlossen wird, das SocketClosed-Ereignis wird ausgelöst, mit dem Grund für die Schließung seiner Ereignisargument.Ich gehe nicht ins Detail, wie der Socket-Wrapper implementiert wird, aber Sie können den Quellcode für diesen Artikel herunterladen und sehen ihre Umsetzung (archive.msdn.microsoft.com/mag201309WPFTP).

FTP verbinden und trennen

Wenn der FTP-Client mit dem FTP-Server eine Verbindung herstellt, wird einen Befehlskanal mit dem Server über dem Befehle und ihre Reaktionen geteilt werden.In der Regel FTP verwendet Port 21, aber aus Gründen der Sicherheit oder andere Faktoren, eine andere Portnummer konfiguriert werden kann.Bevor ein Benutzer Dateien freigeben kann, muss er an den Server, in der Regel mit einem Benutzernamen und Passwort authentifizieren.Wenn die Authentifizierung erfolgreich ist, antwortet der Server (in meinem Fall): 220 Microsoft FTP-Dienst.

Vor dem Verbinden mit dem FTP-Server, erstelle ich ein Objekt namens FtpClient basierend auf mein FTP Client Bibliothek-Klasse zusammen mit Ereignishandler für die Ereignisse, die wie oben beschrieben:

FtpClient ftpClient = null;
async private void btnLogin_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ftpClient = new FtpClient(txtIp.Text, this.Dispatcher);
  // Add event-handlers to various events of ftpClient object
  await ftpClient.ConnectAsync();
}

Sobald das Objekt erstellt, und verschiedene Ereignishandler des FTP-Clients hinzugefügt werden, nenne ich die ConnectAsync-Methode des Objekts FtpClient. So funktioniert ConnectAsync:

public async Task ConnectAsync()
{
  if (!IsConnected)
  {
    logger.AddLog("FTP Command Channel Initailized");
    await FtpCommandSocket.PrepareSocket();
  }
}

ConnectAsync verbindet sich mit dem FTP-Server als ein Socket-Verbindungsvorgang. Wenn der FTP-Client erfolgreich eine Verbindung zum FTP-Server herstellt, wird das FtpConnected-Ereignis ausgelöst, um den Client zu benachrichtigen, den es mit dem FTP-Server verbunden hat:

async void ftpClient_FtpConnected(object sender, EventArgs e)
{
  // Handle the FtpConnected event to show some prompt to
  // user or call AuthenticateAsync to authenticate user
  // automatically once connected
}

Abbildung 2 zeigt, was die app aussieht, wie wenn ein Benutzer erfolgreich auf den FTP-Server verbunden hat.

Connecting and Authenticating to the FTP Server
Abbildung 2 eine Verbindung herstellen und authentifizieren zum FTP-Server

Beachten Sie, dass beim anderen FTP-Servern herstellen, der Text sehen Sie ganz anderes sein kann.

Es gibt drei Möglichkeiten, von einem ftpserver getrennt werden:

  • Der Benutzer sendet einen QUIT-Befehl an den Server bewusst die Verbindung schließen.
  • Nachdem er für eine bestimmte Zeit im Leerlauf, schließt der FTP-Server die Verbindung.
  • An einem oder beiden Enden auftreten, einen Socket-Fehler oder ein interner Fehler aufgetreten.

Um absichtlich über den QUIT-Befehl zu trennen, I QUIT in den Befehl Textfeld Heft und die Send-Befehl-Taste Tap-Ereignishandler ruft die DisconnectAsync-Methode:

async private void btnFtp_Tap(object sender, 
    System.Windows.Input.GestureEventArgs e)
{
  ... 
       if (txtCmd.Text.Equals("QUIT"))
  {
    logger.Logs.Clear();
    await ftpClient.DisconnectAsync();
    return;
  }
  ... 
      }

Dies ist, wie DisconnectAsync den Befehl zum FTP-Server sendet:

public async Task DisconnectAsync()
{
  ftpCommand = FtpCommand.Logout;
  await FtpCommandSocket.SendData("QUIT\r\n");
}

Sobald die Verbindung geschlossen wird, wird das FtpDisconnected-Ereignis ausgelöst, sodass der Client kann das trennen-Ereignis behandeln und anmutig Geben Sie die Ressourcen, mit dem Ergebnis angezeigt, die Abbildung 3:

void ftpClient_FtpDisconnected(object sender,
    FtpDisconnectedEventArgs e)
{
  // Handle FtpDisconnected event to show some prompt
  // or message to user or release
}

Disconnecting from the FTP Server
Abbildung 3 trennen vom FTP-Server

In keinem der genannten Szenarien der FTP-Client wird getrennt vom FTP-Server und alle laufenden FTP-Dateitransfer-Operationen werden auch abgebrochen.

FTP-Authentifizierung

Der Benutzer muss vor Betriebsbeginn alle FTP Datei zum FTP-Server zu authentifizieren. Es gibt zwei Arten von Benutzern: anonym und nicht-anonyme (die mit Anmeldeinformationen). Wenn der FTP-Server von jedem zugegriffen werden kann, werden die Benutzer als anonym authentifiziert. Für sie der Benutzername ist "Anonym" und das Kennwort kann beliebigen Text als E-mail-Adresse formatiert werden. Für nicht-anonyme FTP müssen Benutzer gültige Anmeldeinformationen bereitstellen. Alle Authentifizierungsschema zu implementieren, müssen Sie wissen, welche Art der Authentifizierung auf dem Server aktiviert ist.

Die Authentifizierung erfolgt über zwei FTP-Befehle, USER und PASS. Der USER-Befehl wird verwendet, um die Identität des Benutzers, d.h. den Benutzernamen zu senden. Der PASS-Befehl wird verwendet, um das Kennwort angeben. Obwohl es empfiehlt des Benutzers E-mail-Adresse angeben, funktioniert Text formatiert als E-mail-Adresse:

[FTP Client]: USER anonymous
[FTP Server:] 331 Anonymous access allowed, send identity (e-mail name) as password
[FTP Client]: PASS m@m.com
[FTP Server]: 230 User logged in

Die AuthenticateAsync-Methode hat zwei Überladungen. Die erste Überladung wird die Benutzerauthentifizierung mit standardmäßigen Anmeldeinformationen, die in der Regel für die anonyme Authentifizierung verwendet werden. Die zweite Überladung nimmt einen Benutzernamen und ein Kennwort für die Authentifizierung an dem FTP-Server.

Um den Benutzer automatisch als anonymer Benutzer zu authentifizieren, rufe ich AuthenticateAsync, wenn das FtpConnected-Ereignis empfangen wird:

async void ftpClient_FtpConnected(object sender,
    EventArgs e)
{
  await (sender as FtpClient).AuthenticateAsync();
}

AuthenticateAsync ruft intern die andere Überladung mit standardmäßigen Anmeldeinformationen:

public async Task AuthenticateAsync()
{
  await AuthenticateAsync("anonymous", m@m.com);
}

Die AuthenticateAsync-Überladung Fragen der Benutzer-Befehl zum FTP-Server zusammen mit dem Benutzernamen aus dem Parameter empfangen:

public async Task AuthenticateAsync(String Username,
    String Password)
{
  ftpCommand = FtpCommand.Username;
  this.Username = Username;
  this.Password = Password;
  logger.AddLog(String.Format("FTPClient -> USER {0}\r\n", 
    Username));
  await FtpCommandSocket.SendData(String.Format("USER {0}\r\n", 
    Username));
}

Wenn die Antwort an den Benutzer-Befehl empfangen wird, wird der PASS-Befehl zusammen mit dem Kennwort empfangen aus dem Parameter ausgegeben (siehe Abbildung 4).

Abbildung 4 senden das Kennwort mit PASS, nach dem USER-Befehl

async void FtpClientSocket_DataReceived(object sender, 
    DataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  logger.AddLog(String.Format("FTPServer -> {0}", Response));
    switch (ftpPassiveOperation)
    {
      ... 
          case FtpPassiveOperation.None:
        switch (ftpCommand)
        {
          case FtpCommand.Username:
          if (Response.StartsWith("501"))
          {
            IsBusy = false;
            RaiseFtpAuthenticationFailedEvent();
            break;
          }
          this.ftpCommand = FtpCommand.Password;
          logger.AddLog(String.Format(
            "FTPClient -> PASS {0}\r\n", this.Password));
          await FtpCommandSocket.SendData(
            String.Format("PASS {0}\r\n", this.Password));
          break;
          ... 
              }
    }
    ... 
         }

Wenn die Authentifizierung erfolgreich ist, wird das FtpAuthenticationSucceeded-Ereignis ausgelöst; ist dies nicht der Fall, das FtpAuthenticationFailed-Ereignis ausgelöst:

void ftpClient_FtpAuthenticationFailed(object sender, 
    EventArgs e)
{
  logger.AddLog("Authentication failed");
}
void ftpClient_FtpAuthenticationSucceeded(object sender, 
    EventArgs e)
{
  logger.AddLog("Authentication succeeded");
}

Verzeichnis durchsuchen

FTP bietet Unterstützung für das Verzeichnis durchsuchen, aber dies hängt davon ab, wie der FTP-Client geschrieben wird, um Verzeichnisinformationen anzuzeigen. Wenn der FTP-Client ein GUI ist, kann die Informationen als ein Verzeichnisbaum angezeigt. Ein Konsolen-Client kann hingegen nur der Verzeichnisliste auf dem Bildschirm angezeigt.

Da gibt es keinen Mechanismus zur Verfügung, damit Sie welches Verzeichnis wissen du bietet FTP einen Befehl, um das gegenwärtige Arbeitsverzeichnis (das aktuelle Verzeichnis) zu zeigen. Senden den Befehl PWD zum FTP-Server über den Befehlskanal bietet Ihnen den gesamten Pfad von der Wurzel in das aktuelle Verzeichnis. Wenn ein Benutzer authentifiziert wird, landet er in das Root-Verzeichnis oder im Verzeichnis für ihn in die Konfiguration des FTP-Servers konfiguriert.

Sie können den PWD-Befehl ausgeben, durch Aufrufen von GetPresentWorkingDirectoryAsync. Um das aktuelle Verzeichnis zu erhalten, ich PWD im Befehl Textfeld ausgeben und rufen Sie GetPresentWorkingDirectoryAsync in die Send-Befehl-Taste tippen Sie Ereignis:

async private void btnFtp_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ... 
       if (txtCmd.Text.Equals("PWD"))
  {
    logger.Logs.Clear();
    await ftpClient.GetPresentWorkingDirectoryAsync();
    return;
  }
  ... 
       }

Intern, sendet GetPresentWorkingDirectoryAsync über den Socket PWD zum FTP-Server:

public async Task GetPresentWorkingDirectoryAsync()
{
  if (!IsBusy)
  {
    ftpCommand = FtpCommand.PresentWorkingDirectory;
    logger.AddLog("FTPClient -> PWD\r\n");
    await FtpCommandSocket.SendData("PWD\r\n");
  }
}

Wenn dieser Vorgang erfolgreich ist, der FTP-Server sendet die Antwort mit dem Pfad des Arbeitsverzeichnisses, anwesend, der FTP-Client benachrichtigt, und das FtpPresentWorkingDirectoryReceived-Ereignis wird ausgelöst. Mit den Ereignisargumenten, der Client kann erhalten Sie die Informationen über den Weg das gegenwärtige Arbeitsverzeichnis (siehe Abbildung 5):

void ftpClient_FtpPresentWorkingDirectoryReceived(object sender,
  FtpPresentWorkingDirectoryEventArgs e)
{
  // Handle PresentWorkingDirectoryReceived event to show some
  // prompt or message to user
}

Getting the Present Working Directory from the FTP Server
Abbildung 5 immer die Gegenwart Arbeitsverzeichnis vom FTP-Server

Um in einem anderen Verzeichnis zu ändern, kann der FTP-Client den Working Directory ändern (CWD)-Befehl verwenden. Beachten Sie, dass CWD nur, funktioniert um in ein Unterverzeichnis im aktuellen Arbeitsverzeichnis oder das übergeordnete Verzeichnis zu ändern. Wenn der Benutzer im Root-Verzeichnis ist, er kann nicht rückwärts navigieren und CWD löst einen Fehler als Antwort.

Um das Verzeichnis zu ändern, ich geben Sie den Befehl CWD Befehl Textfeld und in Hahn-Ereignis der Befehl Senden-Schaltfläche rufe ich die ChangeWorkingDirectoryAsync-Methode:

async private void btnFtp_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ... 
       if (txtCmd.Text.StartsWith("CWD"))
  {
    logger.Logs.Clear();
    await ftpClient.ChangeWorkingDirectoryAsync(
      txtCmd.Text.Split(new char[] { ' ' },
      StringSplitOptions.RemoveEmptyEntries)[1]);
    return;
  }
  ... 
       }

Intern gibt ChangeWorkingDirectoryAsync den CWD-Befehl zum FTP-Server:

public async Task ChangeWorkingDirectoryAsync(String RemoteDirectory)
{
  if (!IsBusy)
  {
    this.RemoteDirectory = RemoteDirectory;
    ftpCommand = FtpCommand.ChangeWorkingDirectory;
    logger.AddLog(String.Format("FTPClient -> CWD {0}\r\n", 
        RemoteDirectory));
    await FtpCommandSocket.SendData(String.Format("CWD {0}\r\n", 
        RemoteDirectory));
  }
}

Wenn der Benutzer rückwärts navigieren möchte, kann er senden doppelte Punkte ".." als Parameter für dieses Verfahren. Wenn die Änderung erfolgreich ist, der Kunde informiert wird und das FtpDirectoryChangedSucceeded-Ereignis wird ausgelöst, die in zu sehen Abbildung 6. Wenn CWD ausfällt und eine Fehlermeldung als Antwort sendet, der Client mitgeteilt, dass der Ausfall und das FtpDirectoryChangedFailed-Ereignis ausgelöst:

void ftpClient_FtpDirectoryChangedSucceded(object sender,
  FtpDirectoryChangedEventArgs e)
{
  // Handle DirectoryChangedSucceeded event to show
  // some prompt or message to user
}
void ftpClient_FtpDirectoryChangedFailed(object sender,
  FtpDirectoryChangedEventArgs e)
{
  // Handle DirectoryChangedFailed event to show
  // some prompt or message to user
}

Changing Working Directory on the FTP Server
Abbildung 6 ändern Arbeitsverzeichnis auf dem FTP-Server

Hinzufügen von Unterstützung für Passive Befehle

Jetzt es Zeit zu passiven Befehle unterstützen, ist weil ich möchte, dass den FTP-Server alle Datenverbindungen zur Übertragung von Daten zum und vom Server zu verwalten. Im passiven Modus muss alle Befehle, die zum Übertragen von Daten muss eine Datenverbindung, initiiert vom FTP-Server verwenden. Der FTP-Server wird eine Datenverbindung erstellen und die Socketinformationen für die Datenverbindung zu senden, damit der Client damit verbunden auch einige Datenübertragung-Operationen ausführen kann.

Der passive Modus ist vorübergehender Natur. Bevor Sie ein Kommando zum FTP-Server, die senden oder empfangen von Daten als Antwort senden, der Kunde muss den Server in den passiven Modus sagen — und muss hierzu für jeden Befehl gesendet. Kurzum, für jeden Datentransfer, zwei Befehle werden immer ausgelöst — passiv und dann der Befehl selbst.

Der FTP-Server angewiesen, bei Passiv-Modus über den PASV-Befehl vorzubereiten. Als Antwort sendet er die IP-Adresse und Port-Nummer der Datenverbindung in einem codierten Format. Die Antwort wird decodiert und der Client dann bereitet die Datenverbindung mit dem FTP-Server mit der decodierte IP Adresse und Port. Sobald der Datenvorgang abgeschlossen ist, wird diese Datenverbindung geschlossen und aufgelöst, so dass sie wieder verwendet werden kann. Dies geschieht jedes Mal der PASV-Befehl zum FTP-Server gesendet wird.

Dekodierung der PASV-Befehl-Antwort die Antwort auf den PASV-Befehl sieht folgendermaßen aus:

227 Entering Passive Mode (192,168,33,238,255,167)

Die Antwort hat darin enthaltenen Daten-Kanalinformationen. Basierend auf diese Antwort, ich habe die Socketadresse formulieren — der IP-Adresse und Daten-Port der FTP-Server verwendet für die Datenübertragung-Operation. Hier sind die Schritte, die IP-Adresse und den Port, zu berechnen, wie im Abbildung 7:

  • Die ersten vier Integer-Gruppen bilden die IPV4-Adresse des FTP-Servers; Das heißt, 192.168.33.238.
  • Die restlichen Zahlen umfassen den Data-Port. Sie UMSCHALT links die fünfte Ganzzahl Gruppe mit 8 und führen Sie dann bitweise OR-Operation mit der sechsten Ganzzahl-Gruppe. Der Endwert gibt Ihnen die Port-Nummer, wo wird der Datenkanal verfügbar sein.

Passive Command Request and Reply
Abbildung 7 Passive Befehlsanforderung und Antwort

Abbildung 8 zeigt den Code, der die Antwort analysiert und extrahiert die IP-Adresse und Port-Nummer für den Datenkanal Endpunkt. Die Methode PrepareDataChannelAsync erhält die rohe FTP-Antwort für den PASV-Befehl. Manchmal kann die Antwortzeichenfolge auf dem FTP-Server gehören einige weitere Parameter geändert werden, aber im Wesentlichen die Antwort sendet nur die IP-Adresse und Port Nummer.

Abbildung 8, die Analyse der Daten-Kanal Endpunkt

private async Task PrepareDataChannelAsync(String ChannelInfo)
{
  ChannelInfo = ChannelInfo.Remove(0, "227 Entering Passive Mode".Length);
  // Configure the IP Address
  String[] Splits = ChannelInfo.Substring(ChannelInfo.IndexOf("(") + 1,
    ChannelInfo.Length - ChannelInfo.IndexOf("(") - 5).Split(
      new char[] { ',', ' ', },
  StringSplitOptions.RemoveEmptyEntries);
  String Ipaddr = String.Join(".", Splits, 0, 4);
  // Calculate the Data Port
  Int32 port = Convert.ToInt32(Splits[4]);
  port = ((port << 8) | Convert.ToInt32(Splits[5]));
  logger.AddLog(String.Format(
    "FTP Data Channel IPAddress: {0}, Port: {1}", Ipaddr, port));
  // Create data channel here with extracted IP Address and Port number
  logger.AddLog("FTP Data Channel connected");
}

Weitere Befehle: Liste, Art, STOR und RETR

Den Verzeichnisinhalt auflisten um den Inhalt des aktuellen Arbeitsverzeichnisses aufzulisten, ich senden den LIST-Befehl über den Befehlskanal zum FTP-Server. Der FTP-Server senden jedoch seine Reaktion auf dieses Kommando über den Datenkanal, also ich muss zuerst schicken den PASV-Befehl, um die Datenverbindung zu schaffen, über die der Befehl LIST-Antwort gesendet wird. Das Directory-Listing-Format wird vom Betriebssystem beruhen auf dem FTP-Server installiert ist. Das heißt, wenn der FTP-Server OS Windows ist, wird die Verzeichnislisten im Windows-Verzeichnis-Liste-Format empfangen werden, und wenn das Betriebssystem Unix-basierte ist, wird die Verzeichnisliste im Unix-Format empfangen werden.

Zum Auflisten des Verzeichnisinhalts des gegenwärtigen Arbeitsverzeichnisses I Heft den LIST-Befehl in den Befehl Textfeld und Send-Befehl das Ereignis der Schaltfläche tippen, rufen Sie die GetDirectoryListingAsync-Methode:

async private void btnFtp_Tap(object sender,
  System.Windows.Input.GestureEventArgs e)
{
  ...
      if (txtCmd.Text.Equals("LIST"))
  {
    logger.Logs.Clear();
    await ftpClient.GetDirectoryListingAsync();
    return;
  }
  ... 
       }

Intern, sendet GetDirectoryListingAsync den PASV-Befehl zum FTP-Server über den Socket:

public async Task GetDirectoryListingAsync()
{
  if (!IsBusy)
  {
    fileListingData = null;
    IsBusy = true;
    ftpCommand = FtpCommand.Passive;
    logger.AddLog("FTPClient -> PASV\r\n");
    await FtpCommandSocket.SendData("PASV\r\n");
  }
}

Sobald die Endpunktinformationen für den PASV-Befehl empfangen wird, wird die Datenverbindung erstellt und der LIST-Befehl wird dann zum FTP-Server ausgegeben, wie in Abbildung 9.

Abbildung 9 Verarbeitung des List-Befehls in der Buchse DataReceived-Ereignis

async void FtpClientSocket_DataReceived(object sender, D
    ataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  ... 
      IsBusy = true;
  DataReader dataReader = new DataReader(
    FtpDataChannel.InputStream);
  dataReader.InputStreamOptions = InputStreamOptions.Partial;
  fileListingData = new List<byte>();
  while (!(await dataReader.LoadAsync(1024)).Equals(0))
  {
    fileListingData.AddRange(dataReader.DetachBuffer().ToArray());
  }
  dataReader.Dispose();
  dataReader = null;
  FtpDataChannel.Dispose();
  FtpDataChannel = null;
  String listingData = System.Text.Encoding.UTF8.GetString(
    fileListingData.ToArray(), 0, fileListingData.ToArray().Length);
  String[] listings = listingData.Split(
    new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
  List<String> Filenames = new List<String>();
  List<String> Directories = new List<String>();
  foreach (String listing in listings)
  {
    if (listing.StartsWith("drwx") || listing.Contains("<DIR>"))
    {
      Directories.Add(listing.Split(new char[] { ' ' }).Last());
    }
    else
    {
      Filenames.Add(listing.Split(new char[] { ' ' }).Last());
    }
  }
  RaiseFtpDirectoryListedEvent(Directories.ToArray(),
    Filenames.ToArray());
  ... 
      fileListingData = new List<byte>();
  ftpPassiveOperation = FtpPassiveOperation.ListDirectory;
  logger.AddLog("FTPClient -> LIST\r\n");
  await FtpCommandSocket.SendData("LIST\r\n");
  ... 
       }

Wenn die Verzeichnisliste über den Datenkanal empfangen wird, das FtpDirectoryListed-Ereignis wird ausgelöst, mit der Liste der Dateien und Verzeichnisse in den Ereignisargumenten (Abbildung 10 zeigt die Liste und PASV-Befehle senden und Abbildung 11 wird die Ausgabe der Verzeichnisliste angezeigt):

void ftpClient_FtpDirectoryListed(object sender, 
    FtpDirectoryListedEventArgs e)
{
  foreach (String filename in e.GetFilenames())
  {
    // Handle the name of filenames in current working directory
  }
  foreach (String directory in e.GetDirectories())
  {
    // Handle the name of directories in current working directory
  }
}

Sending LIST Command to the FTP Server
Abbildung 10 LIST-Befehl an den FTP-Server senden

Displaying FileSystem Objects in Response to LIST Command
Abbildung 11 Dateisystem Objekte als Reaktion auf Listenbefehl anzeigen

Beschreibt das Datenformat The TYPE-Befehl wird verwendet, um das Datenformat zu beschreiben, in dem die Daten der Datei empfangen oder gesendet werden. Es erfordert keine PASV-Modus. Ich benutze das "Ich" Formatieren mit dem TYPE-Befehl um eine Image-Datentyp-Datenübertragung zu kennzeichnen. Typ wird in der Regel beim Speichern oder Abrufen von Dateien über eine Datenverbindung verwendet. Dies sagt dem FTP-Server, dass die Übertragung im binary-Modus und nicht im Text oder einige Daten tragende passieren wird. Andere Modi, die mit dem TYPE-Befehl verwendet werden können, finden Sie unter RC 959 für FTP.

Speichern einer Datei auf dem FTP-Server um den Inhalt einer Datei (befindet sich auf einem Gerät) auf einen ftpserver zu speichern, ich schicke dem STOR-Befehl zusammen mit dem Namen der Datei (und Erweiterung) das FTP Server verwenden, um erstellen und speichern Sie die Datei. Die Übertragung von den Inhalt der Datei erfolgt über eine Datenverbindung also, vor dem Senden STOR, ich werde die Endpunktdetails vom FTP-Server mit dem PASV-Befehl Abfrage. Nachdem Sie der Endpunkt empfangen, ich schicke STOR, und wenn ich die Antwort darauf erhalte, ich schicke den Dateiinhalt in binärer Form an dem FTP-Server über die Datenverbindung.

STOR-Befehl kann durch Aufrufen der UploadFileAsync-Methode gesendet werden, wie in Abbildung 12. Diese Methode nimmt zwei Parameter — das Stream-Objekt der lokalen Datei und den Namen der Datei auf dem FTP-Server als Zeichenfolge.

Abbildung 12 die UploadFileAsync-Methode

async private void btnFtp_Tap(object sender, 
    System.Windows.Input.GestureEventArgs e)
{
  ... 
      if (txtCmd.Text.StartsWith("STOR"))
  {
    logger.Logs.Clear();
    String Filename = txtCmd.Text.Split(new char[] { ' ', '/' },      
      StringSplitOptions.RemoveEmptyEntries).Last();
    StorageFile file =
      await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync(
      txtCmd.Text.Split(new char[] { ' ' },
      StringSplitOptions.RemoveEmptyEntries)[1]);
    await ftpClient.UploadFileAsync(await file.OpenStreamForReadAsync(),
      "video2.mp4");
    return;
  }
  ... 
      }

Intern gibt die UploadFileAsync-Methode einen PASV-Befehl zum FTP-Server die Daten Kanal Endpunktinformationen abrufen:

public async Task UploadFileAsync(
  System.IO.Stream LocalFileStream, String RemoteFilename)
{
  if (!IsBusy)
  {
    ftpFileInfo = null;
    IsBusy = true;
    ftpFileInfo = new FtpFileOperationInfo(LocalFileStream, 
        RemoteFilename, true);
    ftpCommand = FtpCommand.Type;
    logger.AddLog("FTPClient -> TYPE I\r\n");
    await FtpCommandSocket.SendData("TYPE I\r\n");
  }
}

Siehe Abbildung 13, den PASV-Befehl als Antwort in das DataReceived-Ereignis der STOR-Befehl ausgegeben, nachdem der Datenkanal erstellt und geöffnet; dann die Datenübertragung beginnt vom Client zum Server.

Abbildung 13 STOR Befehlsverarbeitung in der Buchse DataReceived-Ereignis

async void FtpClientSocket_DataReceived(
  object sender, DataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  ... 
      IsBusy = true;
  DataWriter dataWriter = new DataWriter(
    FtpDataChannel.OutputStream);
  byte[] data = new byte[32768];
  while (!(await ftpFileInfo.LocalFileStream.ReadAsync(
    data, 0, data.Length)).Equals(0))
  {
    dataWriter.WriteBytes(data);
    await dataWriter.StoreAsync();
    RaiseFtpFileTransferProgressedEvent(
      Convert.ToUInt32(data.Length), true);
  }
  await dataWriter.FlushAsync();
  dataWriter.Dispose();
  dataWriter = null;
  FtpDataChannel.Dispose();
  FtpDataChannel = null;
  ... 
       await PrepareDataChannelAsync(Response);
  ftpPassiveOperation = FtpPassiveOperation.FileUpload;
  logger.AddLog(String.Format("FTPClient -> STOR {0}\r\n",
    ftpFileInfo.RemoteFile));
  await FtpCommandSocket.SendData(String.Format("STOR {0}\r\n",
    ftpFileInfo.RemoteFile));
  ... 
      }

Während die Datei-Upload ist, ist der Kunde über den Upload-Fortschritt über FtpFileTransferProgressed informiert:

void ftpClient_FtpFileTransferProgressed(
  object sender, FtpFileTransferProgressedEventArgs e)
{
  // Update the UI with some progressive information
  // or use this to update progress bar
}

Wenn der Datei-Upload-Vorgang erfolgreich die FtpFile abgeschlossen wurde­UploadSucceeded-Ereignis wird ausgelöst. Ist dies nicht der Fall, das FtpFileUploadFailed-Ereignis wird ausgelöst, mit dem Argument der Fehlerursache:

void ftpClient_FtpFileUploadSucceeded(
  object sender, FtpFileTransferEventArgs e)
{
  // Handle UploadSucceeded Event to show some
  // prompt or message to user
}
void ftpClient_FtpFileUploadFailed (
  object sender, FtpFileTransferEventArgs e)
{
  // Handle UploadFailed Event to show some
  // prompt or message to user
}

Abbildung 14 und Abbildung 15 den Prozess der Upload einer Datei auf dem FTP-Server angezeigt.

Uploading a File to the FTP Server
Abbildung 14 das Uploaden einer Datei auf dem FTP-Server

Successful File Upload
Abbildung 15 erfolgreichen Datei-Upload

Abrufen einer Datei von einem FTP-Serverum den Inhalt einer Datei von einem ftpserver abrufen, senden ich den RETR-Befehl zusammen mit dem Namen der Datei und seine Ausdehnung auf den FTP-Server (vorausgesetzt, die Datei in das aktuelle Verzeichnis auf dem Server ist). Die Übertragung von den Inhalt der Datei wird eine Datenverbindung, also vor dem Senden RETR verwenden, ich werde die Endpunktdetails vom FTP-Server mit dem PASV-Befehl Abfrage. Sobald der Endpunkt empfangen wird, ich schicke den RETR-Befehl, und nach Erhalt der Antwort darauf, werde ich den Inhalt der Datei im Binärformat vom FTP-Server abrufen, über den Daten-Verbindung.

Siehe Abbildung 16, der RETR-Befehl kann durch Aufrufen der DownloadFileAsync-Methode gesendet werden. Diese Methode nimmt zwei Parameter — das Stream-Objekt der lokalen Datei zum Speichern des Inhalts und der Name der Datei auf dem FTP-Server als eine Zeichenfolge.

Abbildung 16 der DownloadFileAsync-Methode

async private void btnFtp_Tap(
  object sender, System.Windows.Input.GestureEventArgs e)
{
  ... 
      if (txtCmd.Text.StartsWith("RETR"))
  {
    logger.Logs.Clear();
    String Filename = txtCmd.Text.Split(new char[] { ' ', '/' },
      StringSplitOptions.RemoveEmptyEntries).Last();
    StorageFile file =
      await Windows.Storage.ApplicationData.Current.LocalFolder. 
                                CreateFileAsync(
      txtCmd.Text.Split(new char[] { ' ' }, StringSplitOptions. 
          RemoveEmptyEntries)[1],
      CreationCollisionOption.ReplaceExisting);
    await ftpClient.DownloadFileAsync(
      await file.OpenStreamForWriteAsync(), Filename);
    return;
  }
  ... 
       }

Intern gibt die DownloadFileAsync-Methode einen PASV-Befehl an den FTP-Server, um den Datenkanal Endpunktinformationen abzurufen:

public async Task DownloadFileAsync(
  System.IO.Stream LocalFileStream, String RemoteFilename)
{
  if (!IsBusy)
  {
    ftpFileInfo = null;
    IsBusy = true;
    ftpFileInfo = new FtpFileOperationInfo(
      LocalFileStream, RemoteFilename, false);
    ftpCommand = FtpCommand.Type;
    logger.AddLog("FTPClient -> TYPE I\r\n");
    await FtpCommandSocket.SendData("TYPE I\r\n");
  }
}

Siehe Abbildung 17, den PASV-Befehl als Antwort in das DataReceived-Ereignis wird der RETR-Befehl ausgestellt, nachdem der Datenkanal erstellt und geöffnet; Danach beginnt die Datenübertragung, ausgeführt vom Server zum Client.

Abbildung 17 Verarbeitung den RETR-Befehl in der Buchse DataReceived-Ereignis

async void FtpClientSocket_DataReceived(
  object sender, DataReceivedEventArgs e)
{
  String Response = System.Text.Encoding.UTF8.GetString(
    e.GetData(), 0, e.GetData().Length);
  ... 
       IsBusy = true;
  DataReader dataReader = new DataReader(FtpDataChannel.InputStream);
  dataReader.InputStreamOptions = InputStreamOptions.Partial;
  while (!(await dataReader.LoadAsync(32768)).Equals(0))
  {
    IBuffer databuffer = dataReader.DetachBuffer();
    RaiseFtpFileTransferProgressedEvent(databuffer.Length, false);
    await ftpFileInfo.LocalFileStream.WriteAsync(
      databuffer.ToArray(), 0, Convert.ToInt32  (databuffer.Length));
  }
  await ftpFileInfo.LocalFileStream.FlushAsync();
  dataReader.Dispose();
  dataReader = null;
  FtpDataChannel.Dispose();
  FtpDataChannel = null;
  ...
      await PrepareDataChannelAsync(Response);
  ftpPassiveOperation = FtpPassiveOperation.FileDownload;
  logger.AddLog(String.Format("FTPClient -> RETR {0}\r\n",
    ftpFileInfo.RemoteFile));
  await FtpCommandSocket.SendData(String.Format("RETR {0}\r\n",
    ftpFileInfo.RemoteFile));
  ... 
      }
}

Während die Datei heruntergeladen wird, wird der Client über den Downloadstatus über FtpFileTransferProgressed informiert:

void ftpClient_FtpFileTransferProgressed(object sender,
  FtpFileTransferProgressedEventArgs e)
{
  // Update the UI with some progressive information or use
  // this to update progress bar
}

Wenn die Datei Download-Vorgang erfolgreich abgeschlossen wurde, wird das FtpFileDownloadSucceeded-Ereignis ausgelöst. Ist dies nicht der Fall, das FtpFileDownloadFailed-Ereignis wird ausgelöst, mit dem Argument der Fehlerursache:

void ftpClient_FtpFileDownloadSucceeded(object sender,
  FtpFileTransferFailedEventArgs e)
{
  // Handle DownloadSucceeded event to show some prompt
  // or message to user
}
void ftpClient_FtpFileDownloadFailed(object sender,
  FtpFileTransferFailedEventArgs e)
{
  // Handle UploadFailed Event to show some prompt
  // or message to user
}

Abbildung 18 und Abbildung 19 zeigt den Prozess des Herunterladens einer Datei vom FTP-Server.

Downloading a File from the FTP Server
Abbildung 18 Downloaden einer Datei von der FTP-Server

Successful File Download
Abbildung 19 erfolgreiche Dateidownload

Zusammenfassung

Beachten Sie, dass Sie eine URI-Zuordnung zu einer Implementierung von FTP, app bereitstellen können, so dass andere apps auch die FTP-Dienst und Anfrage-Daten vom FTP-Server zugreifen können. Finden Sie mehr Informationen dazu auf der Seite Windows Phone Dev Center "Auto-Start-Anwendungen mittels Datei und URI Verbände für Windows Phone 8," am bit.ly/XeAaZ8, und Beispielcode auf bit.ly/15x4O0y.

Der Code, die ich geschrieben habe, für meine FTP-Bibliothek und Client app für Windows Phone wird vollständig unter Windows unterstützt 8.x, da ich keine API verwendet haben, die nicht mit Windows kompatibel ist 8.x. Sie können entweder den Code neu kompilieren, für Windows 8.x, oder legen Sie sie in eine Portable Klasse Bibliothek (PCL), die beide Plattformen abzielen kann.

Uday Gupta ist senior Engineer - Produktentwicklung für Symphony Teleca Corp. (Indien) Pvt Ltd. Er hat Erfahrung in vielen .NET Technologien, insbesondere im Windows Presentation Foundation (WPF), Silverlight, Windows Phone und Windows 8. Die meiste Zeit verbrachte, Codierung, gaming, neue Dinge zu lernen und anderen zu helfen.

Dank der folgenden technischen Experten für die Überprüfung dieses Artikels: Tony Meister (Champion DS) und Andy Wigley (Microsoft)
Tony Champion ist Präsident der Champion DS, ist Microsoft-MVP, und aktiv in der Gemeinschaft als Sprecher, Blogger und Autor. Er führt einen Blog unter tonychampion.net und ist erreichbar per E-mail unter tony@tonychampion.net.

Andy Wigley ist ein Technical Evangelist für Microsoft UK arbeiten. Andy ist bekannt für die beliebte Windows Phone JumpStart-Videos, die verfügbaren channel9.msdn.com und ist ein regelmäßiger Referent bei wichtigen Konferenzen wie der Tech- Ed.