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

File Transfer using WCF and Socket

, 7 Jun 2009 CPOL
Rate this:
Please Sign up or sign in to vote.
This article will show how to transfer some data from one point to another

Introduction

This article will show you how to write a specific program for transferring files and folder through network using WCF (Windows Communication Foundation) and TCP clients.

Algorithm

  • When program runs, we start a new WCF service on the specific port with number 7634
  • Each client has his own proxy, which provides functionality for working with this service.
  • One client connects to another with this proxy and calls TransferClient.SendQuery(string ip, FilesTransfer transfer,string sport)
  • If another client accepts it begins to exchange files data through the Sockets on the random port?.

Beginning

All logic of a file transfer shares on two main parts - communicating with client's WCF service and working with Sockets. So, I'll try to explain how it works in the real program.

First Step - WCF Service

If you don't know what is WCF and how it works, I strongly recommend you to read an article, Windows Communication Foundation FAQ quick starter Part 1.
All web service code is located in ITransfer.cs file. The service interface provides one method SendQuery, which is necessary for inquiry to the client.

[ServiceContract(SessionMode = SessionMode.Required)]
    public interface ITransfer
    {
        [OperationContract]
        IEnumerable<object> SendQuery(string ip, FilesTransfer transfer, int sport);
    }

As you can see, the SessionMode attribute is set as Required. It means that for each client, a separate session will be created.
Also in this piece of code, the FilesTransfer class is involved. It is used for reception of information about files by another client. Realisation is resulted below:

[DataContract]
    public class FilesTransfer
    {
        [DataMember]
        public Action Action { get; set; } // The action of the packet
        [DataMember]
        public string Name { get; set; } //Client name
        [DataMember]
        public string Id { get; set; } //Identification
        [DataMember]
        public List<Transfer> Files { get; set; } //the list of the file

        public FilesTransfer()
        {
            Files = new List<transfer />();
        }

        public FilesTransfer(Action action)
            : this()
        {
            Action = action;
        }
    }

Let's stop on the list of files. It is a simple generic enumeration of Transfer class. This class comprises information on each file or the catalogue separately. If the list contains a folder, it means that it also contains all files and directories below on the hierarchy tree. It is reached by the recursive algorithm:

[DataContract]
    public class Transfer
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public long Length { get; set; }
        [DataMember]
        public string Catalog { get; set; }
        [DataMember]
        public string Id { get; set; }
        [DataMember]
        public string FullPath { get; set; }
    }
private List<Transfer> RecurseFolder(string root, string folderName)
        {
            List<Transfer> result = new List<Transfer>();
            DirectoryInfo info = new DirectoryInfo(root + folderName); //get information
						// about current directory
            foreach (FileInfo file in info.GetFiles()) // get all files from 
						// "folderName" folder
            {
                Transfer transfer = new Transfer();
                transfer.Catalog = folderName.Substring(1); //without slash sign
                transfer.Id = Guid.NewGuid().ToString() + Guid.NewGuid().ToString();
                transfer.Length = file.Length;
                transfer.Name = file.Name;
                transfer.FullPath = file.FullName;
                result.Add(transfer);
            }
            foreach (DirectoryInfo dir in info.GetDirectories())
            {
                //recursively get all files from directories
	       result.AddRange(RecurseFolder(root, folderName + @"\" + dir.Name)); 
	   }
            return result;
        }

Well, when FilesTransfer class is full of necessary data, let's look at how the SendQuery method implements in the web service. Having received the necessary information, after the MessageBox.Show() request, the new TProgress form is created and client socket port is generated. The client is completely prepared for the data transferring to begin.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
        ConcurrencyMode = ConcurrencyMode.Multiple, 
	UseSynchronizationContext = false, IncludeExceptionDetailInFaults = true)]
    public class TransferClass : ITransfer
    {
        AutoResetEvent tcontinue = new AutoResetEvent(false);
        TProgress form;

        public IEnumerable<object> SendQuery
		(string ip, FilesTransfer transfer, int sport)
        {
            int port = 0;
            switch (transfer.Action)
            {
                case Action.Invite: 
                    {
                        port = HelpClass.GetAvailablePort(); 	//getting free port 
							//from the system
                        if (MessageBox.Show(string.Format("Accept {0} files from {1}", 
			transfer.Files.Count, transfer.Name), "Files", 
			MessageBoxButtons.YesNo) == DialogResult.Yes)
                        {
                            ThreadStart start = delegate
                            {
                                form = new TProgress(null, port);
                                form.sport = sport;
                                form.Show();
                                form.GetThread = new Thread
				(new ParameterizedThreadStart(form.GetInvoke));
                                form.GetThread.Start(transfer);
                            };
                            HelpClass.Form.BeginInvoke(start); //if we call thread 
					// by "this.Invoke()" the parental stream 
					// would be lost and we couldn't turn to 
					// other elements of a "form" class
                            break;
                        }
                        else return new List<object>() { (int)InviteRusult.Cancel, 0 };
                    }
                default:
                    {
                        return new List<object>() { (int)InviteRusult.Busy, 0 };
                    }
            }
            return new List<object>() { (int)InviteRusult.Ok, port };
        }
    }

Second Step - Socket Transferring

Now when both ports for a socket are known, clients connect to each other and begin to transfer byte data. But it isn't a model like "client-server" because when one file is passed the socket which was a server for the one moment becomes a client and also a client becomes a server. It is necessary because the server does not know when the client has completely accepted the handed over information and that there are no overloads or information leakage. It stops the action till the moment when the client will inform that all is accepted. If the type is directory, client simply creates it in the necessary folder. Let's look at the server side sender:

public void SendInvoke(object obj)
        {
            try
            {
                FilesTransfer ft = (FilesTransfer)(obj as List<object>)[0]; //get file 
					//information which you want to transfer
                ThreadStart ts = delegate //invocation of the main thread
                {
                    progressBar1.Maximum = (int)(ft.Files.Sum(t => t.Length) / 4);
                };
                progressBar1.Invoke(ts, null);
                IPEndPoint sendTo = (IPEndPoint)(EndPoint)(obj as List<object>)[1];//get
				// the address of the client we want to connect
                TcpClient client = new TcpClient(new IPEndPoint(IPAddress.Any, sport));
                client.BeginConnect(sendTo.Address, sendTo.Port, 
		new AsyncCallback(Connected), null); //opening connection
                connected.WaitOne(); //sleep till the client answered
                long sended = 0;
                foreach (TransferNamespace.Transfer t in ft.Files)
                {
                    using (FileStream stream = new FileStream
			(t.FullPath, FileMode.Open, FileAccess.Read, FileShare.Read))
                    {
                        ts = delegate
                        {
                            progressBar2.Value = 0;
                            progressBar2.Maximum = (int)t.Length;
                            textBox1.Text = t.Name;
                        };
                        this.Invoke(ts, null);
                        client.SendBufferSize = 65 * (int)Math.Exp
				(6 * System.Math.Log(10)); //like 65 10^6
                        sended = 0;
                        stream.Seek(0, SeekOrigin.Begin); //seek the pointer 
						   //to the begin of the file
                        while (true)
                        {
                            byte[] bytes; //we will send the data by 20000 sized packets
                            if (sended >= stream.Length) break;
                            if (stream.Length - sended < 20000)
                                bytes = new byte[stream.Length - sended]; //if it is 
							//the last packet
                            else
                                bytes = new byte[20000];
                            sended += bytes.Length; //increment the counter of 
						// the sended data
                            ThreadStart asdasd = delegate
                            {
                                progressBar1.Value += (int)bytes.Length / 4;
                                progressBar2.Value += (int)bytes.Length;
                            };
                            this.Invoke(asdasd, null);
                            stream.Read(bytes, 0, bytes.Length);
                            client.Client.Send(bytes, SocketFlags.Partial); //sending 
							//by "streaming" mode
                        }
                    }
                    client.Client.Receive(new byte[1]); //sleep for accepting 
					//that client completely got the file
                }
                client.Close();
                ts = delegate
                {
                    textBox1.Text = "Sended.";
                };
                this.Invoke(ts, null);
            }
            catch { MessageBox.Show("Aborted"); }
        }

And client:

public void GetInvoke(object obj)
        {
            try
            {
                string path = Application.StartupPath + @"\Files\"; //get the full 
						//path to the file location
                byte[] overthrow = new byte[0]; //for the surplus data
                FilesTransfer tr = (FilesTransfer)obj;
                ThreadStart callback = delegate
                {
                    progressBar1.Maximum = (int)tr.Files.Sum(t => t.Length) / 4;
                };
                progressBar1.Invoke(callback, null);
                TcpListener listener = new TcpListener(port);
                listener.Start();
                TcpClient client = listener.AcceptTcpClient();
                callback = delegate
                {
                    this.Text = string.Format("Getting from {0}", 
		   (client.Client.RemoteEndPoint as IPEndPoint).Address.ToString());
                };
                this.Invoke(callback, null);
                client.ReceiveBufferSize = 65 * (int)Math.Exp(6 * System.Math.Log(10));
                foreach (TransferNamespace.Transfer t in tr.Files)
                {
                    if (t.Catalog != "<root>" && !Directory.Exists(path + t.Catalog))
                        Directory.CreateDirectory(path + t.Catalog);
                    FileStream stream = new FileStream(path + 
			(t.Catalog == "<root>" ? "" : t.Catalog) + 
			t.Name, FileMode.OpenOrCreate, FileAccess.Write);
                    ThreadStart todo = delegate
                    {
                        progressBar2.Value = 0;
                        progressBar2.Maximum = (int)t.Length;
                        textBox1.Text = t.Name;
                    };
                    this.Invoke(todo, null);
                    long getted = 0;
                    if (overthrow.Length > 0) //If there are surpluses of the data, 
			//we are going to put it in the beginning of the new file
                    {
                        int gotted = 0;
                        if (overthrow.Length <= t.Length)
                        {
                            stream.Write(overthrow, 0, overthrow.Length);
                            gotted = overthrow.Length; //increment and start for 
						//normally getting other data
                        }
                        else //it means that we got the data of two files. 
				//It can be if files are very small
                        {
                            byte[] otherbuf = overthrow.Without(0, t.Length);
                            stream.Write(otherbuf, 0, otherbuf.Length);
                            overthrow = overthrow.Without(t.Length);
                            gotted = otherbuf.Length;
                        }
                        stream.Flush(); //writing in the file
                        getted = gotted;
                        todo = delegate
                        {
                            try
                            {
                                progressBar1.Value += (int)gotted / 4;
                                progressBar2.Value += (int)gotted;
                            }
                            catch { }
                        };
                        this.Invoke(todo, null);
                    }
                    while (getted < t.Length)
                    {
                        byte[] buffer = new byte[client.Client.Available]; //get new 
								  //bytes
                        client.Client.Receive(buffer, SocketFlags.Partial);
                        getted += buffer.Length; //increment the counter
                        if (getted > t.Length) //if we got more data that needs 
			//for current file, it must be located in the specified array
                        {
                            long length = (long)getted - t.Length;
                            overthrow = buffer.Without(buffer.Length - length);
                            buffer = buffer.Without(0, buffer.Length - length);
                            getted -= length;
                        }
                        todo = delegate
                        {
                            try
                            {
                                progressBar1.Value += (int)buffer.Length / 4;
                                progressBar2.Value += (int)buffer.Length;
                            }
                            catch { }
                        };
                        this.Invoke(todo, null);
                        stream.Write(buffer, 0, buffer.Length);
                        stream.Flush();
                    }
                    stream.Dispose();
                    client.Client.SendTo(new byte[1] { 1 }, 
			new IPEndPoint((client.Client.RemoteEndPoint 
			as IPEndPoint).Address, sport)); //continue sending data
                }
                client.Close();
                callback = delegate
                {
                    textBox1.Text = "Getted.";
                };
                this.Invoke(callback, null);
            }
            catch { MessageBox.Show("Aborted"); }
        }

Conclusion

All in all, pluses in this architecture are that we could keep many connections and transferring processes. Using such technologies as WCF, we don't care about serialization of objects and working with channels. There are ways of transferring a stream through web service, but it is not our choice. Socket provides us with a high speed, not only client-server architecture and more comfortable interface.

History

  • 7th June, 2009: Initial post

License

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

Share

About the Author

Artem S. Dmitriev
Software Developer
Russian Federation Russian Federation
No Biography provided

Comments and Discussions

 
Questionqurey about sending file to another remote computer on network PinmemberMember 1031223127-Nov-13 19:53 
AnswerRe: qurey about sending file to another remote computer on network Pinmemberfanorng19852-Dec-13 17:13 
SuggestionGood Article Pinmembergarlapally15-Sep-12 22:49 
Question[My vote of 2] how to run the demo Pinmemberimak12-May-12 9:19 
QuestionFolder Transfer PingroupNavin Kumar K Subramanian1-May-12 21:47 
GeneralMy vote of 5 Pinmemberdelibey9-Feb-12 14:00 
thx very good article
QuestionFile transfer Pinmembermohanrajkiller11-Nov-11 1:03 
GeneralThanks Pinmemberzevassyrossa6-May-11 23:15 
QuestionCan you give us the source code of the web service? Pinmemberkaphwankim29-Nov-10 13:24 
GeneralMy vote of 4 PinmemberSummer Xia17-Jul-10 5:51 
GeneralPlease help with service connecting PinmemberChauNTran12-Jul-10 14:09 
GeneralSubdirectory creation with: "&lt root &gt" PinmemberElBuitre7-Apr-10 23:21 
GeneralRe: Subdirectory creation with: "&lt root &gt" Pinmembermartyn_mcfly8-Apr-10 2:25 
GeneralRe: Subdirectory creation with: "&lt root &gt" PinmemberElBuitre8-Apr-10 3:51 
GeneralRe: Subdirectory creation with: "&lt root &gt" Pinmembermartyn_mcfly10-Apr-10 22:04 
GeneralPeer-to-peer twist Pinmemberbleekay6-Apr-10 19:06 
GeneralAlternative recursive scan PinmemberAnton Levshunov25-Nov-09 5:43 
GeneralExplanation of when and how the service reference was created Pinmemberbubk12-Nov-09 5:50 
QuestionUsing BasicHttpBinding PinmemberOdedk1-Nov-09 5:47 
GeneralIt doesn't work... Pinmembermancardo11-Sep-09 16:27 
GeneralRe: It doesn't work... PinmemberLucio Flavio25-Feb-10 7:34 
GeneralNeed more explaination PinmemberMoePOo2-Sep-09 16:01 
JokeThanks Pinmemberhamed3242124-Jun-09 20:15 
GeneralRe: Thanks Pinmembermartyn_mcfly24-Jun-09 23:16 
GeneralNice Article, thanks PinmemberJon-Hawkins8-Jun-09 1:16 
AnswerRe: Nice Article, thanks Pinmembermartyn_mcfly8-Jun-09 2:15 

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
Web03 | 2.8.141223.1 | Last Updated 7 Jun 2009
Article Copyright 2009 by Artem S. Dmitriev
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid