File copying on Network showing with Asynchronous Progress Bar in Console Application

The following example demonstrate how to copy files from TcpClient to TcpListener on network asycnhronously. While copying files asynchronously, percentage of copied files are also updated asynchronously.

if cancellation is needed, application can be cancelled by Ctrl + c keys.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp6
{
    class Program
    {
        static string sourcePath = @"C:\SourceDir";
        static string destinationPath = @"C:\TestCopy";
        static int consoleTop = 1;
        static char[] progressCharacters = @"/-\|".ToArray();

        static void Main(string[] args)
        {
            MainAsync().Wait();
        }

        static async Task MainAsync()
        {
            CancellationTokenSource cts = new CancellationTokenSource();

            Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;

                cts.Cancel();
            };

            if (Directory.Exists(destinationPath))
                Directory.Delete(destinationPath, true);

            Directory.CreateDirectory(destinationPath);

            string host = "127.0.0.1";

            int port = 8080;

            Task tcpListenerTask = StartTcpListener(host, port, cts.Token);

            Task tcpClientTask = StartTcpClient(host, port, cts.Token);

            await Task.WhenAll(tcpListenerTask, tcpClientTask);
        }

        static async Task StartTcpListener(string ip, int port, CancellationToken token)
        {
            TcpListener tcpListener = new TcpListener(IPAddress.Parse(ip), port);

            tcpListener.Start(2);

            TcpClient acceptedTcpClient;

            string fileName;

            int receiveBufferSize = 8 * 1024;

            int bytesRead;

            long totalBytesRead = 0;

            byte[] bytesBuffer;

            string fileNameAndSize;

            int fileNameBytesCount;

            string[] fileNameAndSizeArray;

            bool firstCall = true;

            string message;

            while (!token.IsCancellationRequested)
            {
                try
                {
                    acceptedTcpClient = await tcpListener.AcceptTcpClientAsync().ContinueWith(t => t.Result, token);

                    using (NetworkStream networkStream = acceptedTcpClient.GetStream())
                    {
                        bytesBuffer = new byte[1024];

                        bytesRead = await networkStream.ReadAsync(bytesBuffer, 0, 1024, token);

                        fileNameAndSize = Encoding.UTF8.GetString(bytesBuffer, 0, Array.IndexOf(bytesBuffer, (byte)0));

                        fileNameAndSizeArray = fileNameAndSize.Split('|');

                        fileName = fileNameAndSizeArray[0];

                        message = $"Server is receiving bytes from client ({acceptedTcpClient.Client.RemoteEndPoint}) for {fileName} ";

                        int.TryParse(fileNameAndSizeArray[1], out fileNameBytesCount);

                        int currentTop = Interlocked.Increment(ref consoleTop);

                        using (FileStream fileStream = File.OpenWrite(Path.Combine(destinationPath, fileName)))
                        {
                            totalBytesRead = 0;

                            bytesBuffer = new byte[receiveBufferSize];

                            while ((bytesRead = await networkStream.ReadAsync(bytesBuffer, 0, receiveBufferSize, token)) > 0)
                            {
                                await fileStream.WriteAsync(bytesBuffer, 0, bytesRead, token);

                                totalBytesRead += bytesRead;

                                ShowProgress(firstCall, message, totalBytesRead, fileNameBytesCount, currentTop);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }

        static async Task StartTcpClient(string ip, int port, CancellationToken token)
        {
            string[] files = Directory.GetFiles(sourcePath);

            FileInfo sourceFi;

            int sendBufferSize = 8 * 1024;

            int bytesRead;

            long totalBytesRead = 0;

            byte[] bytesBuffer;

            bool firstCall = true;

            string message;

            long fileSize;

            try
            {
                foreach (string sourceFilePath in files)
                {
                    using (TcpClient tcpClient = new TcpClient())
                    {
                        await tcpClient.ConnectAsync(IPAddress.Parse(ip), port);

                        using (NetworkStream networkStream = tcpClient.GetStream())
                        {
                            sourceFi = new FileInfo(sourceFilePath);

                            message = $"Client ({tcpClient.Client.LocalEndPoint}) is sending bytes to server ({tcpClient.Client.RemoteEndPoint}) for {sourceFi.Name} ";

                            fileSize = sourceFi.Length;

                            totalBytesRead = 0;

                            int currentTop = Interlocked.Increment(ref consoleTop);

                            bytesBuffer = new byte[1024];

                            Encoding.UTF8.GetBytes($"{sourceFi.Name}|{sourceFi.Length}").CopyTo(bytesBuffer, 0);

                            await networkStream.WriteAsync(bytesBuffer, 0, 1024, token);

                            firstCall = true;

                            using (FileStream fileStream = sourceFi.OpenRead())
                            {
                                bytesBuffer = new byte[sendBufferSize];

                                while ((bytesRead = await fileStream.ReadAsync(bytesBuffer, 0, sendBufferSize, token)) > 0)
                                {
                                    await networkStream.WriteAsync(bytesBuffer, 0, bytesRead);

                                    totalBytesRead += bytesRead;

                                    ShowProgress(firstCall, message, totalBytesRead, fileSize, currentTop);

                                    firstCall = false;

                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
            }
        }

        static void ShowProgress(bool firstCall, string message, long processed, long total, int cursorTop)
        {
            long percent = (100 * (processed + 1)) / total;
            long characterIndex = (percent + 1) & 3;

            lock (typeof(Console))
            {
                int msgLenght = message.Length;
                if (firstCall)
                {
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.SetCursorPosition(0, cursorTop);
                    Console.Write(message);
                }

                Console.SetCursorPosition(msgLenght, cursorTop);

                if (percent >= 100)
                {
                    percent = 100;
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.Write($"{percent}% [OK]");
                }
                else
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.Out.Write($"{percent}% [{progressCharacters[characterIndex]}]");
                }
            }
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *