Click here to Skip to main content
Click here to Skip to main content

How to copy files remotely

, 12 Jan 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
Two applications to copy files remotely using sockets.

RemoteCopyFiles

Introduction

We have many, many servers at my job. Enterprise applications like SQL Server, Oracle, etc., are installed in them by the system administrators. But when we (the developers) create a new application or a new component, we have to deploy the new assemblies in each server. We don't like this, we hate this, so I tried to make an application which make this task easier for us...

Background

There are two applications. Both of them use sockets to communicate with each other and transfer the file names and data within. These are the applications:

ReceiveFiles

This is a console application that listens for new requests. When a request is sent to a server, this application gets it and copies the file to the desired final path. This is not a Windows service that is listening all the time. I will later explain how to start this thing remotely.

SendFiles

This is a Windows application, with a little GUI, used to start the remote ReceiveFiles application and send the files to the server.

PsExec

PsExec is a Windows application which you can use to start a remote process. It is used to start ReceiveFiles remotely. I can not attach it here, because it's forbidden. But you can download it for free from here: http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx.

How to use it

  1. Copy ReceiveFiles in the servers you want to deploy files. With a little luck, this will be the last time you will have to copy anything by yourself.
  2. You have to download PsExec from the Microsoft web page in order to start ReceiveFiles remotely. Once it is downloaded, you have to copy PsExec in the same path where SendFiles is installed
  3. Populate the configuration file with the server names, and users and passwords.
  4. Start the SendFiles app. In the first text box, "Path where ReceiveFile is...", you have to write the remote path where ReceiveFiles is installed. It's recommended that the path is the same in all machines.
  5. Select the desired servers and click "Start remote listeners" to start the remote ReceiveFiles application.
  6. Once the remote applications are started (you can check it in the MS DOS windows), you can select the files to copy by clicking the "Get Files" button. Choose what you want.
  7. Click the "Send Files" button to copy the selected files to all servers.
  8. Once the process is finished, you can close the remote apps by clicking "Close remote listeners".

That's all...

Using the code

The code is a modification of some code found in the web; so far it works OK!

Once the ReceiveFiles app is started, it will open two sockets in an infinite loop until the application is closed manually or a closing request is received.

The first of the sockets is open by the ReceiveFilename method. It will wait for a file name and a path to start the process. When the data is received (the file name), it will create the path in case it doesn't exist. Later, the ReceiveFile method will open another socket in order to wait for the binary data, which is the content of the file. After the file is created, it will wait for any other request. If the file name is "CLOSE APP", the application will shutdown, because this is the way to close the app remotely.

The core of the ReceiveFiles app is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace RecepcionFicherosSocket
{
    public class SocketsFileTransfer
    {
        private const string STRING_TO_CLOSE = "CLOSE APP";
        public void ReceiveFile(int port, string filepathandname)
        {
            string methodname = "ReceiveFile";
            try
            {
                IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, port);
                Socket sock = new Socket(AddressFamily.InterNetwork, 
                                  SocketType.Stream, ProtocolType.IP);
                sock.Bind(ipEnd);
                sock.Listen(1);
                Socket serverSocket = sock.Accept();
                byte[] data = new byte[1000000];
                int received = serverSocket.Receive(data);
                int filenameLength = BitConverter.ToInt32(data, 0);
                string filename = Encoding.ASCII.GetString(data, 4, filenameLength);
                this.CreateDirectoryFromPath(filepathandname);
                BinaryWriter bWrite = new BinaryWriter(
                              File.Open(filepathandname, FileMode.Create));
                bWrite.Write(data, filenameLength + 4, received - filenameLength - 4);
                int received2 = serverSocket.Receive(data);
                while (received2 > 0)
                {
                    bWrite.Write(data, 0, received2);
                    received2 = serverSocket.Receive(data);
                }
                bWrite.Close();
                serverSocket.Close();
                sock.Close();
                MyLogs.WriteLog(methodname, "File copied ok: " + 
                                filepathandname, false);
            }
            catch (Exception ex)
            {
                MyLogs.WriteLog(methodname, "Port: " + port + 
                                " file: " + filepathandname + 
                                " " + ex.ToString(), true);
            }
        }
        
        public string ReceiveFilename(IPAddress ipAddress, int port)
        {
            string filename = string.Empty;
            string methodname = "ReceiveFilename";
            try
            {
                
                TcpListener tcpListener = new TcpListener(ipAddress, port);
                //Starting the listening
                tcpListener.Start();
                //Wait for a client
                Socket socketForClient = tcpListener.AcceptSocket();
                if (socketForClient.Connected)
                {       
                    // If connected
                    MyLogs.WriteLog(methodname,"Connected",false);
                    NetworkStream networkStream = new NetworkStream(socketForClient);
                    StreamWriter streamWriter = new StreamWriter(networkStream);
                    StreamReader streamReader = new StreamReader(networkStream);            
                    string theString = "Ok so far";
                    //Wait for the client request
                    filename = streamReader.ReadLine();
                    MyLogs.WriteLog(methodname,theString + 
                                    " " + filename,false);
                    //Answering the client
                    streamWriter.WriteLine(theString);
                    streamWriter.Flush();
                    //Close
                    streamReader.Close();
                    streamWriter.Close();
                    networkStream.Close();
                    socketForClient.Close();
                    tcpListener.Stop();
                    //If the client has requested the close of the server
                    if (filename == STRING_TO_CLOSE)
                    {
                        MyLogs.WriteLog(methodname, "Closing requested", false);
                        Environment.Exit(999);
                    }
                }
            }
            catch (Exception ex)
            {
                MyLogs.WriteLog("ReceiveFilename", ex.ToString(), true);
            }
            return filename;
        }
        public IPAddress GetIpFromHostName(string servername)
        {
            IPAddress ip = null;
            String strHostName = string.Empty;
            if (servername == string.Empty)
            {
                strHostName = Dns.GetHostName();
                MyLogs.WriteLog("GetIpFromHostName", 
                       "Machine name: " + strHostName,false);
            }
            else
            {
                strHostName = servername;
            }
            // Then using host name, get the IP address list..
            IPHostEntry ipEntry = Dns.GetHostByName(strHostName);
            IPAddress[] addr = ipEntry.AddressList;
            ip = addr[0];
            return ip;
        }
        private void CreateDirectoryFromPath(string path)
        {
            string directoryPath = System.IO.Path.GetDirectoryName(path);
            if (Directory.Exists(directoryPath) == false)
            {
                try
                {
                    Directory.CreateDirectory(directoryPath);
                    MyLogs.WriteLog("CreateDirectoryFromPath", 
                                    "Directory: " + directoryPath + 
                                    " created", false);
                }
                catch (Exception ex)
                {
                    MyLogs.WriteLog("CreateDirectoryFromPath", 
                          "Cant create directory for: " + path + 
                          " " + ex.ToString(), true);
                }
            }
        }
    }
}

The main point of the ReceiveFiles app will make a non-stop execution of the application, until it is closed or a closing request is received.

The main point of the ReceiveFiles app is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace RecepcionFicherosSocket
{
    class Program
    {
        static void Main(string[] args)
        {
            ReceiveFiles ns = new ReceiveFiles();
            SocketsFileTransfer sft = new SocketsFileTransfer();
            ReceiveFiles.ipAddress = sft.GetIpFromHostName(string.Empty);
            ns.Start();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace RecepcionFicherosSocket
{
    public class ReceiveFiles
    {
        public static IPAddress ipAddress;
        public void Start()
        {
            SocketsFileTransfer sockets = new SocketsFileTransfer();
            
            //The file names are received from 9999 port
            int port = 9999;
            string filename = sockets.ReceiveFilename(ipAddress, port);
            //The file data is received from 9998 port
            port = 9998;
            sockets.ReceiveFile(port, filename);
            MyLogs.WriteLog("Start","Process finished",false);
            //Start the process again
            this.Start();
        }
    }
}

The SendFiles app uses two sockets (the same numbers as ReceiveFiles) to send both the file name and the file data (in binary). First of all, it will send the file name to the remote app and wait for a ReceiveFile confirmation. Once the confirmation has arrived, the file is open in binary mode, and the data is read and sent through another socket to ReceiveFiles.

The core of the SendFiles app is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace SendFiles
{
    public class SocketsFileTransfer
    {
        private const string STRING_TO_CLOSE = "CLOSE APP";
        public void SendFileName(string server, int port, string filename)
        {
            TcpClient socketForServer;
            try
            {
                //Creamos un TcpCliente y le pasamos el server y el puerto.
                socketForServer = new TcpClient(server, port);
            }
            catch (Exception ex)
            {
                Console.WriteLine("No se pudo conectar a " + port + 
                                  ":" + server + " " + ex.ToString());
                return;
            }//aqui es lo mismo que en el server. Usamos StreamWriter y Reader.
            NetworkStream networkStream = socketForServer.GetStream();
            StreamReader streamReader = new System.IO.StreamReader(networkStream);
            StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
            try
            {
                streamWriter.WriteLine(filename);
                streamWriter.Flush();
                string outputString = streamReader.ReadLine();
                Console.WriteLine(outputString);
            }
            catch
            {
                Console.WriteLine("Exception reading from Server");
            }
            finally
            {
                networkStream.Close();
            }
        }
        public void SendFile(IPAddress ipAddress, int port, string filenameToUse)
        {
            //send file:
            IPEndPoint ipEnd = new IPEndPoint(ipAddress, port);
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, 
                                             SocketType.Stream, ProtocolType.IP);
            filenameToUse = filenameToUse.Replace("\\", "/");
            byte[] filenameData = Encoding.ASCII.GetBytes(filenameToUse);
            byte[] output = new byte[4 + filenameData.Length];
            BitConverter.GetBytes(filenameData.Length).CopyTo(output, 0);
            filenameData.CopyTo(output, 4);
            clientSocket.Connect(ipEnd);
            clientSocket.SendFile(filenameToUse, output, null, 
                                  TransmitFileOptions.UseDefaultWorkerThread);
            clientSocket.Close();
        }
        public IPAddress GetIpFromHostName(string servername)
        {
            IPAddress ip = null;
            String strHostName = string.Empty;
            if (servername == string.Empty)
            {
                strHostName = Dns.GetHostName();
                MyLogs.WriteLog("GetIpFromHostName", 
                                "Machine name: " + strHostName, false);
            }
            else
            {
                strHostName = servername;
            }
            // Then using host name, get the IP address list..
            IPHostEntry ipEntry = Dns.GetHostByName(strHostName);
            IPAddress[] addr = ipEntry.AddressList;
            ip = addr[0];
            return ip;
        }
    }
}

It would be very annoying to start the remote ReceiveFiles application server by server, manually, before sending the files. So, I use the Microsoft PsExec application to start ReceiveFiles remotely. Basically, I use the Process class to start PsExec and pass the necessary command parameters to start ReceiveFiles in the remote server.

The Send method loops each file and calls SendFileName and SendFile in order to transfer the data.

The Close method sends a particular message as a file name ("CLOSE APP") to request the ReceiveFiles app to close; PsExec is not necessary here, because a previous communication has been established between both the applications.

Let's now see how to start the ReceiveFiles app remotely, send the files, and then close the ReceiveFiles app:

Start the app:

private void btnStartRemoteListeners_Click(object sender, RoutedEventArgs e)
{
    bool bcontinue = true;
    bool wasanyerror = false;
    string psexecPath = System.Windows.Forms.Application.StartupPath + "\\psexec.exe</a />";
    string remoteExecutablePath = txtPathWhereReceiveFilesIs.Text;
    string parameters = string.Empty;
    bool showFinalMessage = true;
    progressBar1.Maximum = lstServers.SelectedItems.Count;
    progressBar1.Value = 0;
    txtLog.Text = String.Empty;
    if (ValidateStart()==true)
    {
        foreach (string serverName in lstServers.SelectedItems)
        {
            Server server = null;
            if (ServersList.TryGetValue(serverName, out server) == true)
            {
                server.User = server.User == "" ? "Error" : server.User;
                server.Password = server.Password == "" ? "Error" : server.Password;
                try
                {
                    if (bcontinue == true)
                    {
                        if (server.MyProcess == null)
                        {
                            Process p = new Process();
                            ProcessStartInfo psinfo = new ProcessStartInfo(psexecPath);
                            parameters = @"\\" + server.ServerName + 
                               " -u " + server.User + " -p " + 
                               server.Password + " -i 0 " + remoteExecutablePath;
                            psinfo.Arguments = parameters;
                            txtLog.AppendText(psexecPath + " " + 
                               parameters + Environment.NewLine);
                            psinfo.CreateNoWindow = true;
                            psinfo.WindowStyle = ProcessWindowStyle.Minimized;
                            p = Process.Start(psinfo);
                            server.MyProcess = p;
                            System.Threading.Thread.Sleep(1000);
                            if (p.HasExited == true)
                            {
                                server.MyProcess = null;
                                throw new Exception();
                            }
                        }
                        else
                        {
                            System.Windows.Forms.MessageBox.Show("The process " + 
                              "is already running at the server: " + 
                              server.ServerName, "Start Listener", 
                              MessageBoxButtons.OK, MessageBoxIcon.Information);
                            showFinalMessage = false;
                        }
                    }
                }
                catch (Exception ex)
                {
                    string msg = "Error starting the remote listener at " + 
                                 server + Environment.NewLine + ex.Message + 
                                 Environment.NewLine + "Path: " + 
                                 psexecPath + Environment.NewLine +
                                 "Parameters: " + parameters + Environment.NewLine +
                                 "Maybe the remote path does not exists, " + 
                                 "the password is incorrect or the remote " + 
                                 "listener is still running";
                    txtLog.AppendText(msg + Environment.NewLine);
                    System.Windows.Forms.MessageBox.Show(msg, "Error", 
                           MessageBoxButtons.OK, MessageBoxIcon.Error);
                    wasanyerror = true;
                    if (System.Windows.Forms.MessageBox.Show(
                        "Do you want to continue?", 
                        "Continue", MessageBoxButtons.YesNo, 
                        MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
                    {
                        bcontinue = false;
                    }
                }
                progressBar1.Value++;
            }
        }
        if (showFinalMessage == true)
        {
            if (wasanyerror == false)
            {
                System.Windows.Forms.MessageBox.Show(
                  "Remote listeners started ok", "Ok", 
                  MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                System.Windows.Forms.MessageBox.Show(
                  "Remote listener started with errors", 
                  "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}

Copy files:

private void btnSendFiles_Click(object sender, RoutedEventArgs e)
{
    SocketsFileTransfer socketsFileTransfer = new SocketsFileTransfer();
    int filenamePort = 9999;  //port number for the file name
    int filedataPort = 9998;  //port number for the file data
    string serversWithError = string.Empty;
    bool bcontinue = true;
    bool wasanerror = false;
    if (this.ValidateSending() == true)
    {
        this.Cursor = System.Windows.Input.Cursors.Wait;
        progressBar1.Maximum = lstServers.SelectedItems.Count;
        progressBar1.Value = 0;
        txtLog.Text = String.Empty;
        foreach (string serverName in lstServers.SelectedItems)
        {
            //Get the IP of the remote server
            System.Net.IPAddress ipAddress = 
              socketsFileTransfer.GetIpFromHostName(serverName);
            txtLog.AppendText(serverName + " ip: " + 
              ipAddress.ToString() + Environment.NewLine);
            foreach (string filePath in filesToSend)
            {
                string fileName = System.IO.Path.GetFileName(filePath);
                string finalPath = cboRemotePaths.Text + "\\" + fileName;
                txtLog.AppendText("Uploading " + fileName + 
                  " to " + serverName + "::" + 
                  finalPath + Environment.NewLine);
                try
                {
                    if (bcontinue == true)
                    {
                        //Send the file name
                        socketsFileTransfer.SendFileName(serverName, filenamePort, finalPath);
                        txtLog.AppendText("File name sent" + Environment.NewLine);
                        //Wait
                        System.Threading.Thread.Sleep(250);
                        //Send the file data
                        socketsFileTransfer.SendFile(ipAddress, filedataPort, filePath);
                        txtLog.AppendText("Data sent" + Environment.NewLine);
                    }
                }
                catch (Exception ex)
                {
                    string msg = "Error sending: " + fileName + 
                      " to " + serverName + Environment.NewLine + ex.Message;
                    txtLog.AppendText(msg);
                    System.Windows.Forms.MessageBox.Show(msg, "Error at " + 
                      serverName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    wasanerror = true;
                    serversWithError += serverName + Environment.NewLine;
                    if (System.Windows.Forms.MessageBox.Show("Do you want " + 
                        "to continue with the send process?", "Continue", 
                        MessageBoxButtons.YesNo, MessageBoxIcon.Question) == 
                        System.Windows.Forms.DialogResult.No)
                    {
                        bcontinue = false;
                    }
                }
            }
            progressBar1.Value++;
            System.Windows.Forms.Application.DoEvents();
        }
        if (wasanerror == false)
        {
            System.Windows.Forms.MessageBox.Show("Process finished ok", 
                   "Ok", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        else
        {
            System.Windows.Forms.MessageBox.Show("Process finished " + 
              "with errors at: " + Environment.NewLine + serversWithError, 
              "Errors", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        this.Cursor = System.Windows.Input.Cursors.Arrow;
    }
}

Close the app:

private void btnCloseRemoteListeners_Click(object sender, RoutedEventArgs e)
{
    bool bcontinue = true;
    bool wasanyerror = false;

    progressBar1.Maximum = lstServers.SelectedItems.Count;
    progressBar1.Value = 0;
    txtLog.Text = String.Empty;
    foreach (string serverName in lstServers.SelectedItems)
    {
        if (bcontinue == true)
        {
            if (this.CloseServer(serverName) == false)
            {
                wasanyerror = true;
                if (System.Windows.Forms.MessageBox.Show(
                    "Do you want to close the remote listeners?", 
                    "Continue", MessageBoxButtons.YesNo, 
                    MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.No)
                {
                    bcontinue = false;
                }
            }
        }
        progressBar1.Value++;
    }
    if (wasanyerror == false)
    {
        System.Windows.Forms.MessageBox.Show("Remote listeners closed", 
           "Ok", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }
    else
    {
        System.Windows.Forms.MessageBox.Show(
          "Remote listeners closed with errors", 
          "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

The configuration file

In this file, you can specify the name, domain, username, and passwords of the servers with which you will work. This data is shown in the list on the left side of the SendFiles form:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <Server>
    <ServerName>machine1.domain.xxx</ServerName>
    <User>domain\user</User>
    <Password>password</Password>
  </Server>
  <Server>
    <ServerName>machine2.domain.xxx</ServerName>
    <User>domain\user</User>
    <Password>password</Password>
  </Server>
</configuration>

Points of interest

Don't hesitate to comment anyway you want. I am here to learn.

History

  • Version 1.0.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Santiago Sanchez
Software Developer
Spain Spain
I´ve been working with Oracle, Sql Server and Visual Basic 6 since 2003 and with C# since 2006. Now I´m fighting with Biztalk 2006 too...
 
MCTS - .NET Framework 4, Windows Applications
MCTS - Accessing Data with .NET Framework 4

Comments and Discussions

 
QuestionProblems during transfer to another machine Pinmembersanprets1-Sep-14 3:30 
AnswerRe: Problems during transfer to another machine PinmemberSantiago Sanchez2-Sep-14 3:42 
QuestionProblem in transfer PinprofessionalAgent_Spock23-Feb-14 21:43 
AnswerRe: Problem in transfer PinmemberSantiago Sanchez23-Feb-14 22:03 
GeneralRe: Problem in transfer PinprofessionalAgent_Spock23-Feb-14 22:36 
GeneralRemote Paths Pinmemberqpon8-Feb-10 10:33 
GeneralRe: Remote Paths PinmemberSantiago Sanchez8-Feb-10 12:20 
GeneralRe: Remote Paths Pinmemberqpon11-Feb-10 22:22 
GeneralRe: Remote Paths PinmemberSantiago Sanchez11-Feb-10 23:55 
GeneralGood Article PinmemberSrinath G Nath13-Jan-10 17:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.1411028.1 | Last Updated 13 Jan 2010
Article Copyright 2010 by Santiago Sanchez
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid