Avatar of pointeman
pointeman
Flag 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.
C#

Avatar of undefined
Last Comment
pointeman

8/22/2022 - Mon
Gary Davis

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
kaufmed

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
SOLUTION
Mike Tomlinson

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
SOLUTION
AlexPace

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
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.
kaufmed

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?
Your help has saved me hundreds of hours of internet surfing.
fblack61
pointeman

ASKER
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.
Mike Tomlinson

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().
kaufmed

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:

FTP Server Log
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.
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
pointeman

ASKER
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
kaufmed

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  : )
pointeman

ASKER
This was difficult to grade, thx
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck