Friday, October 23, 2009

Client to Client file transfer using C# .Net Programming - Remoting technology

For basic information regarding .Net Remoting technology you may refer my previous article “Global Text Chat Room Application Using C#.NET Remoting Technology”.

User manual:



To run that application first you have to run server application. Then you will get one winforms with two buttons ‘Start’ and ‘Stop’ as screenshot. Your have to run server by pressing Start button.

Now run client application. Here you will get first a winform which will ask your name and server address, port etc. If you try with same machine then the given address is ok. But if it different address then need to update ‘localhost’ with particular IP address or host name. But do not change port number and remaining thing and press enter or join button. See screenshot.

Then you will get another form. Here you will see online users in a TreeView. From here you need to select a user by double clicking on his/her name, and then need to select a file by pressing ‘Select a File’ button. A file dialog box window will come from where you need to select a file. Then press Send button. On receiver end one popup will come which will ask receiver will he receive it reject. If select receive then will ask a path where it will save. Then the file will transfer from sender to receiver.





Application logic:

For file transfer I have used basic codes of my previous project. Here also has three part –

(1) Base Remote Class (DLL)

(2) Server Class and

(3) Client Class

Server and Client class is using Base Remote Class.

In Base Remote Class have three sections (region) –

  1. Join to server and user related information
  2. File related information – file name, size, sender name, receiver name etc. and
  3. File transferring between two users.

For online user list I have used ArrayList object and for transferring data have used queue by HashTable.

In Server Class nothing very special. Here have just two methods, for starting server and stopping server. Start server is opening a TCPChannel and registering it, on the other side Stop Server is closing TCPChannel and unregistering it.

For login to server it exactly same as my previous article “Global Text Chat Room Application Using C#.NET Remoting Technology”.

In Client Class, there have few interesting parts. The major is a timer event. By this event client is checking to server if any data/information is available to it or not. Also by that timer collects latest user related information from server.

When client wants to send a file to some one then first it reads file related information and sends it to server with receiver id through SetFileInfo method of RemoteObject. Then it reads the file by a BinaryReader part by part and sends each part to server. In Server side it stores in a Hashtable from which receiver will receives it and server will remove each part one by as and when receiver confirms to remove after receivers’ acceptance. Here major roles plays SendFile method and Timer tick of Client and SendDataToServer, GetDataFromServer method of remote object class.

Basic architecture of my application is that, remaining of all you can understand if you go though the codes.

Remoting / Common base code is here:

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

using System.Collections;


namespace RemoteBase

{

///

/// Sample object to demonstrate the use of .NET Remoting.

///

public class SampleObject : MarshalByRefObject

{

ArrayList alOnlineUser = new ArrayList();

Hashtable htUserData = new Hashtable();

Hashtable htFileData = new Hashtable();

private int key = 0;

private string receiverID,senderID,fileName,fileTransferStatus;

private int fileSize;

private bool isFileTransferring = false;


#region USER JOINING AND ONLINE USER RELATED

public bool JoinToChatRoom(string name)

{

if (alOnlineUser.IndexOf(name) > -1)

return false;

else

{

alOnlineUser.Add(name);

return true;

}

}

public void LeaveChatRoom(string name)

{

alOnlineUser.Remove(name);

}

public ArrayList GetOnlineUser()

{

return alOnlineUser;

}

public int CurrentKeyNo()

{

return key;

}

#endregion


#region FILE INFORMATION ACCESS BETWEEN SENDER AND RECEIVER

public bool SetFileInfo(string senderId, string receiverId, string fileName, int fileSize)

{

if (!this.isFileTransferring)

{

lock (this)

{

this.isFileTransferring = true;

}

this.senderID = senderId;

this.receiverID = receiverId;

this.fileName = fileName;

this.fileSize = fileSize;

return true;

}

else//Now some file transferring, so need to wait

return false;

}

public string GetFileName_Size_SenderId(string receiverId)

{

if (this.receiverID == receiverId)

return this.fileName + ":" + this.fileSize.ToString() + ":" + this.senderID;

else

return null;

}

#endregion



#region FILE TRANSFER BETWEEN SENDER AND RECEIVER


///

/// This method is used to receive data from sender and hold in a array to deliver receiver.

///

///

///

///

///

public bool SendDataToServer(byte[] data, int dataSliceNo, string senderId)

{

lock (this)

{

htFileData.Add(receiverID + dataSliceNo.ToString(), data);//Key= receiverId + sliceNo, binary data

}

return true;

}

// This method is used to receive data from server to receiver.

public byte[] GetDataFromServer(string receiverId, int nextSliceNo)

{

if (htFileData.Contains(receiverID + nextSliceNo.ToString()))

{

lock (this)

{

byte[] tempByteData = (byte[])htFileData[receiverID + nextSliceNo.ToString()];

htFileData.Remove(receiverID + nextSliceNo.ToString());//Key= receiverId + sliceNo

return tempByteData;

}

}

else

return null;

}


///

/// This method is used to declare that file has succesfully received by the receiver. It invokes after the whole file

/// received by the receiver.

///

///

public void ReceiveFileConfirm(string clientID)

{

if (clientID == receiverID)

{

this.fileTransferStatus = "File transfered successfully by " + this.receiverID;

this.receiverID = "";

lock (this)

{

this.isFileTransferring = false;

htFileData.Clear();

}

}

}

public void RejectFile(string clientID)

{

if (this.receiverID == clientID)

{

this.fileTransferStatus = "File has rejected by " + this.receiverID;

this.receiverID = "";

lock (this)

{

this.isFileTransferring = false;

htFileData.Clear();

}

}

}

#endregion

}

}


Server code is here:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

using RemoteBase;

namespace RemoteServer

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}

TcpChannel channel;

private void btnStart_Click(object sender, EventArgs e)

{

if (channel == null)

{

channel = new TcpChannel(8080);

ChannelServices.RegisterChannel(channel, false);

RemotingConfiguration.RegisterWellKnownServiceType(typeof(SampleObject), "HelloWorld", WellKnownObjectMode.Singleton);


lblStatus.Text = "Running...";

btnStart.Enabled = false;

btnStop.Enabled = true;

}

}


private void btnStop_Click(object sender, EventArgs e)

{

if (channel != null)

{

ChannelServices.UnregisterChannel(channel);

channel = null;

lblStatus.Text = "Stopped.";

btnStart.Enabled = true;

btnStop.Enabled = false;

}

}


}

}


Client code is here:

Login and server connecting code:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

using System.Collections;

using RemoteBase;

namespace RemotingClient

{

public partial class frmLogin : Form

{

TcpChannel chan;

ArrayList alOnlineUser = new ArrayList();

frmChatWin objChatWin;

public frmLogin()

{

InitializeComponent();

}


private void btnJoin_Click(object sender, EventArgs e)

{

JoinToChatRoom();

}

private void JoinToChatRoom()

{

if (chan == null && txtName.Text.Trim().Length != 0)

{

chan = new TcpChannel();

ChannelServices.RegisterChannel(chan,false);


// Create an instance of the remote object

objChatWin = new frmChatWin();

objChatWin.remoteObj = (SampleObject)Activator.GetObject(typeof(RemoteBase.SampleObject), txtServerAdd.Text);


if (!objChatWin.remoteObj.JoinToChatRoom(txtName.Text))

{

MessageBox.Show(txtName.Text+ " already joined, please try with different name");

ChannelServices.UnregisterChannel(chan);

chan = null;

objChatWin.Dispose();

return;

}

objChatWin.key = objChatWin.remoteObj.CurrentKeyNo();

objChatWin.myName= txtName.Text;


this.Hide();

objChatWin.Show();

}

}

}

}

File transferring code:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

using System.Collections;

using RemoteBase;

using System.IO;

using System.Threading;

namespace RemotingClient

{

public partial class frmChatWin : Form

{

internal SampleObject remoteObj;

internal int key = 0,fileSize;

int sliceSize = 5 * 1024;

internal string myName,fileName;

ArrayList alOnlineUser = new ArrayList();

string selectedUserName;

OpenFileDialog ofd;

public frmChatWin()

{

InitializeComponent();

}


private void btnSend_Click(object sender, EventArgs e)

{

SendFile();

}

private void timer1_Tick(object sender, EventArgs e)

{

timer1.Stop();

if (remoteObj != null)

{

string receiveFileName, senderId;

int fileSize;

string FileName_Size = remoteObj.GetFileName_Size_SenderId(myName);

if (FileName_Size != null)

{


string[] recvFileDesc = FileName_Size.Split(':');

receiveFileName = recvFileDesc[0];

fileSize = int.Parse(recvFileDesc[1]);

senderId = recvFileDesc[2];


DialogResult usrRes = MessageBox.Show(senderId + " want to send a file. Will you accept it?", "", MessageBoxButtons.YesNo);

if (usrRes == DialogResult.Yes)

{

FolderBrowserDialog fbdSelect = new FolderBrowserDialog();

fbdSelect.Description = "Select a path to save received file.";

if (fbdSelect.ShowDialog() == DialogResult.OK)

{

BinaryWriter bWrite = new BinaryWriter(new FileStream(fbdSelect.SelectedPath+"\\" + receiveFileName, FileMode.Append));

for (int i = 0; i * sliceSize <= fileSize; )

{

byte[] buffer = remoteObj.GetDataFromServer(myName, i + 1);//i+1 because when data send to server the it starts from 1

if (buffer != null)

{

bWrite.Write(buffer);

i++;

}

}

bWrite.Close();

remoteObj.ReceiveFileConfirm(myName);

MessageBox.Show("File received successfully.");

}

}

else

{

remoteObj.RejectFile(myName);

}

}

ArrayList onlineUser = remoteObj.GetOnlineUser();

foreach (string name in onlineUser)

{

if (name != myName && !tvOnlineUser.Nodes.ContainsKey(name))

tvOnlineUser.Nodes.Add(name, name);

}

}

//**** Button Enable - Disable

if (tvOnlineUser.Nodes.Count > 0 && lblSelUser.Text.Length > 15)

btnSelect.Enabled = true;

else

btnSelect.Enabled = false;

btnSend.Enabled = false;

if (ofd != null)

if (ofd.FileName.Length > 0)

btnSend.Enabled = true;

timer1.Start();

}

private void SendFile()

{

if (remoteObj != null)

{

BinaryReader bRead = new BinaryReader(new FileStream(ofd.FileName, FileMode.Open));

fileSize = (int)bRead.BaseStream.Length;

if (fileSize > 1024 * 1024 * 50)

MessageBox.Show("You can send maximum 50 MB file.", "Limit cross!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

else

{


byte[] smallPiece;


if (fileSize <>

{

smallPiece = new byte[fileSize];

bRead.Read(smallPiece, 0, fileSize);

}

else//File is more than 5KB

{

smallPiece = new byte[sliceSize];

if (remoteObj.SetFileInfo(myName, selectedUserName, fileName, fileSize))

{

for (int i = 1; i * sliceSize <= fileSize + sliceSize; )

{

if (i * sliceSize <= fileSize) // Last slice yet not reached

{

bRead.Read(smallPiece, 0, sliceSize);

if (remoteObj.SendDataToServer(smallPiece, i, myName))

i++;

}

else//Last slice of data is going to fetch and for last slice data remains less than 5KB

{

int remainDataSize = fileSize - ((i - 1) * sliceSize);

smallPiece = new byte[remainDataSize];

bRead.Read(smallPiece, 0, remainDataSize);

if (remoteObj.SendDataToServer(smallPiece, i, myName))

i++;

}

}

}//End of SetFileInfo

}

}

bRead.Close();

}

}

private void Form1_FormClosed(object sender, FormClosedEventArgs e)

{

if (remoteObj != null)

{

remoteObj.LeaveChatRoom(myName);

}

Application.Exit();

}


private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)

{

System.Diagnostics.Process.Start("iexplore.exe", "http://socketprogramming.blogspot.com");

}

private void btnSelect_Click(object sender, EventArgs e)

{

ofd = new OpenFileDialog();

if (ofd.ShowDialog() == DialogResult.OK)

{

lblSelectedFile.Text = "Selected File: "+ofd.SafeFileName;

fileName = ofd.SafeFileName;

}

}


private void tvOnlineUser_DoubleClick(object sender, EventArgs e)

{

selectedUserName = tvOnlineUser.SelectedNode.Name;

lblSelUser.Text = "Selected User: "+selectedUserName;

}

}

}