Link to home
Start Free TrialLog in
Avatar of pointeman
pointemanFlag for United States of America

asked on

FTP Sockets Async vs Sync?

I currently have a nice FTP app using FtpWebResponse. Now I want to try C# Socket programming for a FTP client.

Q. Do you recommend Asynchronous or Synchronous sockets?

The synchronous Client code I've seen seems to appears to lockout any other FTP clients requests. Whereas asynchronous Client code seems more server friendly. Your thoughts please.

I don't think FtpWebResponse is either asynchronous or synchronous.
Avatar of Gary Davis
Gary Davis
Flag of United States of America image

For Windows 8 or Windows Phone 8 you can use the StreamSocket for async socket communications that you can use to implement your FTP. Something like this which also includes timeout checking.

using System;
using System.Threading;
using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

public class SocketService
{
    private static StreamSocket Socket { get; set; }

    /// <summary>
    /// Call like this: var socket = await ConnectToTivo();
    /// Socket can then be used for reaw/writes
    /// </summary>
    /// <returns>Connected Socket or null if timeout/error</returns>
    public static async Task<StreamSocket> ConnectSocket(string ip, string port)
    {
        if (Socket != null) return Socket; // Already connected

        Socket = new StreamSocket();

        var cancelToken = new CancellationTokenSource();
        cancelToken.CancelAfter(2000); // Two second timeout

        try
        {
            var op1 = Socket.ConnectAsync(new HostName(ip), port);
            var task = op1.AsTask(cancelToken.Token);
            await task;
        }
        catch (TaskCanceledException timeoutEx)
        {
            // TODO: Handle timeout
            return null;
        }
        catch (Exception ex)
        {
            // TODO: Handle exception
            return null;
        }

        // Do you expect any data after connect? If so, read it
        var reader = new DataReader(Socket.InputStream) {InputStreamOptions = InputStreamOptions.Partial};

        cancelToken = new CancellationTokenSource();
        cancelToken.CancelAfter(2000);

        try
        {
            var op2 = reader.LoadAsync(100);
            var task = op2.AsTask((cancelToken.Token));
            await task;
        }
        catch (TaskCanceledException timeoutEx)
        {
            // TODO: Handle timeout
            return null;
        }
        catch (Exception ex)
        {
            // TODO: Handle exception
            return null;
        }

        var data = reader.ReadString(reader.UnconsumedBufferLength);
        // Do something with data after connect
        return Socket;
    }
}

Open in new window


Once you have the Socket, you can then use similar code to read/write on that socket or close the socket (Socket.Dispose()).

Gary Davis
ASKER CERTIFIED SOLUTION
Avatar of kaufmed
kaufmed
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of pointeman

ASKER

After some research I'm finding that FtpWebResponce is a synchronous class, hence it doesn't have actual login method. Each upload, download, list member is quick and dirty then connection closed.. It will also freeze a desktop app if not running on a separate thread.
You are correct, the FTP server doesn't care if my code is synchronous or asynchronous. This a matter of block or not blocking threads.
I'm very familiar with the complexities of FTP commands worked with these servers many times. This is a personal project so I'm not concerned with the boss. I do understand your suggestion about plugins.
I've worked with TCP sockets and Server/Client programming before and find it very interesting. My original post is my concern on communicating with an FTP server, sync or async socket code.
Thank you for your input.
So is your question more or less about keeping your application responsive? If so, is there any reason why you wouldn't keep the FTP stuff on another thread? Why do you think sockets would help you here?
I will have more creativity using Sockets when dealing with FTP commands wheras FtpWebRequest does not. In fact a newly created URI requires a login if not using anonymous. So changing a directory is a pain. Sockets will provide raw FTP command control. I digress.
Anyway, this post has given me some ideas.
Ok...you can just use TcpClient to connect to the server then.  Everything is sent back and forth as ASCII.  Convert your string commands to a byte arrays using something like:

    byte[] cmd = System.Text.ASCIIEncoding.ASCII.GetBytes("FTP Command Here");

You can convert responses received as byte arrays back to a string using System.Text.ASCIIEncoding.ASCII.GetString().
Honestly, I am wondering if you have set up your code in the correct way. You shouldn't need to log in to the server again unless you have forcibly severed the connection (or it has been dropped for some uncontrollable reason). Take for example the following:

using System;
using System.IO;
using System.Net;

namespace _27980937
{
    class Program
    {
        static void Main(string[] args)
        {
            FtpWebRequest request = FtpWebRequest.Create("ftp://kenneth@localhost:21") as FtpWebRequest;
            FtpWebResponse response;

            request.Method = WebRequestMethods.Ftp.ListDirectory;

            response = request.GetResponse() as FtpWebResponse;
            Console.WriteLine(response.StatusDescription);

            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                while (!reader.EndOfStream)
                {
                    Console.WriteLine(reader.ReadLine());
                }
            }

            request = FtpWebRequest.Create("ftp://kenneth@localhost:21/test") as FtpWebRequest;
            request.Method = WebRequestMethods.Ftp.MakeDirectory;
            response = request.GetResponse() as FtpWebResponse;
            Console.WriteLine(response.StatusDescription);

            request = FtpWebRequest.Create("ftp://kenneth@localhost:21/test") as FtpWebRequest;
            request.Method = WebRequestMethods.Ftp.ListDirectory;

            response = request.GetResponse() as FtpWebResponse;
            Console.WriteLine(response.StatusDescription);

            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                while (!reader.EndOfStream)
                {
                    Console.WriteLine(reader.ReadLine());
                }
            }

            request = FtpWebRequest.Create("ftp://kenneth@localhost:21/test/example.txt") as FtpWebRequest;
            request.Method = WebRequestMethods.Ftp.UploadFile;

            using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
            {
                writer.WriteLine("hello world!");
            }

            response = request.GetResponse() as FtpWebResponse;

            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                while (!reader.EndOfStream)
                {
                    Console.WriteLine(reader.ReadLine());
                }
            }

            request = FtpWebRequest.Create("ftp://kenneth@localhost:21/test") as FtpWebRequest;
            request.Method = WebRequestMethods.Ftp.ListDirectory;

            response = request.GetResponse() as FtpWebResponse;
            Console.WriteLine(response.StatusDescription);

            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                while (!reader.EndOfStream)
                {
                    Console.WriteLine(reader.ReadLine());
                }
            }
        }
    }
}

Open in new window


And the conversation:

User generated image
As you can see, only one USER and PASS commands have been sent for the whole session.

You might also look into the ConnectionGroupName and KeepAlive properties to help ensure only one connection is used.
The original post is "FTP Sockets Async vs Sync?". I'm satisfied with the Socket question.

I think we need to pickup this prior EE question concerning FtpWebRequest. Thank you Kaufmed for all your help. https://www.experts-exchange.com/questions/27974557/FtpWebRequest-Double-Login.html
The impression I got was that you wanted to use sockets because you felt you were limited in the functionality of FtpWebRequest/FtpWebResponse. If it is more of a learning issue, then by all means proceed with the socket approach (I've done so myself in the past). If this is because you want a custom FTP client, then I think the work involved is going to be much greater when dealing with raw sockets than with the existing FTP classes.

In either case, good luck, and I (and others, I'm sure) are happy to help where I can  : )
This was difficult to grade, thx