Introduction

UPDATE: 3.30.2014 There was a bug in the retrieve function (with files bigger than 512bytes), this is fixed.

An FTP client begins by settings up a socket to the FTP server, authorize itself and then continues by sending instructions. All communication is done in plain text which makes the protocol both easy to understand and use.

Using the active and passive stream sockets, many ftp clients were imagined and developed in .net. However, the sockets implementatin in Windows Runtime (Windows Store Apps) has changed dramatically.

This sample gives at least access to the user the basic commands implementation using socket connections to an ftp server to execute ftp commands (e.g. STOR, RETR, MKD, RMD, DELE...)

Running the Sample

There are no third party libraries used in this sample.

IMPORTANT : To run the tests project on local server (i.e. If you are running a local ftp server), you have to disable the loopback check for the test project application. (How to enable Loopback and troubleshoot network isolation)

Easiest way to achieve this is to start debugging one of the unit tests, and use the Enable Loopback Utility to locate the test project and disable the loopback check.

Description

 The sample uses an abstraction layer (IFtpChannel) implemented in FtpStreamChannel and only created but not implemented in FtpWebChannel.

The FtpStreamChannel creates and connects to the sockets according to the commands the user wants to send which are organized in Messages namespace. (i.e. FtpCreateDirectoryRequest, FtpPassiveModeRequest...).

Each socket method is implemented in async pattern so as a developer you would need to be carefull about the asyc/awaits.

Forexample in order to open the initial socket, you would need to use the host name for the ftp server and "21" as the service name. Since we are not dealing with an FTPS or SFTP server, we can use the PlainSocket protection level to open our initial socket.

 

C#
Edit|Remove
public async Task<bool> ConnectAsync(HostName host, string service) 
{ 
    m_StreamSocket = new StreamSocket(); 
 
    try 
    { 
        await m_StreamSocket.ConnectAsync(host, service, SocketProtectionLevel.PlainSocket).AsTask(); 
 
        m_IsConnected = true; 
    } 
    catch (Exception ex) 
    { 
        var socketError = SocketError.GetStatus(ex.HResult); 
         
        // TODO: log error 
         
        return false; 
    } 
 
    return true; 
}
When there is a need for expecting a reply for the command that we sent. For instance if we were to set the username create a directory, you can use the ExecuteAsync<TReply> method.

 

 

C#
Edit|Remove
public async Task<T> ExecuteAsync<T>(FtpRequest request) where T:FtpResponse 
{ 
    if(!IsConnected) 
        throw new FtpException("Not Connected"); 
 
    await WriteLineAsync(Encoding, request.Command); 
 
    await FlushAsync(); 
 
    return await GetReplyAsync<T>(); 
}
 

 

This will write the command you want to send in the opened socket and read the response from the server.

The higher level functions are just about using the ExecuteAsync function and expecting a reply.

On top of the FtpStreamChannel, there is an FtpClient implementation that implements higher level functions using the methods exposed by the IFtpChannel.

For instance, RetrieveFile method uses the streamed storage file approach, so that the files that are downloaded from the ftp server can easily be shared through the share contract.

 

C#
Edit|Remove
        public async Task<StorageFile> RetrieveFileAsync(string path) 
        { 
            if (!(await FileExistsAsync(path))) 
            { 
                throw new FileNotFoundException("FTP file could not be retrieved", path); 
            } 
 
            StorageFile resultantFile; 
            FtpResponse reply; 
 
            // 
            // A more efficient way, maybe a DataReader can be used here 
            using (var stream = await OpenReadAsync(path)) 
            { 
                var buffer = new byte[512].AsBuffer(); 
                var resultingBuffer = new byte[0]; 
 
                while (true) 
                { 
                    IBuffer readBuffer = await stream.ReadAsync(buffer, 512, InputStreamOptions.Partial); 
 
                    if (readBuffer.Length == 0break; 
 
                    resultingBuffer = resultingBuffer.Concat(readBuffer.ToArray()).ToArray(); 
                } 
 
                resultantFile = await StorageFile.CreateStreamedFileAsync(path.GetFtpFileName(), async (fileStream) => 
                { 
                    await fileStream.WriteAsync(resultingBuffer.AsBuffer()); 
                    fileStream.FlushAsync(); 
                    fileStream.Dispose(); 
                }, null); 
            } 
 
            if (!(reply = await CloseDataStreamAsync(null)).Success) 
            { 
                throw new FtpCommandException(reply); 
            } 
 
            return resultantFile; 
        }
 The unit tests project presents examples for different usages of FTP client.

 

Source Code Files

More Information

Wait for the blog entry on http://canbilgin.wordpress.com/

Project is inspired by http://netftp.codeplex.com/