Reputation: 1083
I have to develop a simple TCP File Transfer Client as part of an exercise/demonstration but I am having trouble with data delivery.
I am already provided with the TCP File server which will receive the incoming files and I have to code a Client that will connect to and send a file to the server. Thus far I have been successful as far as converting the data of the selected file into to a format ready to transferring and successfully opening a connection and then sending of the data, however even encounter a problem on server that is receiving - I am not allowed to change the code of the server and should thus change my client code in such a way that the data sent can be interpreted by the server. Here is the code I use (some is left out which is simple overhead, like input-box to get IP address etc.):
Link for TCP Server VS Solution (if you prefer): https://www.mediafire.com/?682owf9wtdzmxac
TCP File Transfer Client Sending method:
private void TransferFile(string _sFileName, string _sIPAdress)
{
//Convert data for transfer
Stream strmfilestream = File.OpenRead(_sFileName);
Byte[] bFileBuffer = new Byte[strmfilestream.Length];
//Open TCP/IP Connection
TcpClient tcpClientSocket = new TcpClient(_sIPAdress,8080);
NetworkStream nsNetworkStream = tcpClientSocket.GetStream();
nsNetworkStream.Write(bFileBuffer,0,bFileBuffer.GetLength(0));
nsNetworkStream.Close();
}
*Note: _sFileName is just the full file path + File name from a OpenFileDialog.
Here is the Load Method of the server to get things going:
if (!Directory.Exists(@"C:\TCPFileServer"))
Directory.CreateDirectory(@"C:\TCPFileServer");
//Get Ip address of server host machine
IPHostEntry IPHost = Dns.GetHostEntry(Dns.GetHostName());
lblServerIP.Text = IPHost.AddressList[5].ToString();
lstSockets = new ArrayList();
Thread thdListener = new Thread(new ThreadStart(listenerThread));
thdListener.IsBackground = true; //This will enabe the thread to terminate when application is closed
thdListener.Start();
Here is the listener thread method:
public void listenerThread()
{
TcpListener tcpListener = new TcpListener(IPAddress.Any, 8080);
tcpListener.Start();
while (true)
{
Socket handlerSocket = tcpListener.AcceptSocket();
if (handlerSocket.Connected)
{
this.Invoke((Action)(() => lstConnections.Items.Add(handlerSocket.RemoteEndPoint.ToString() + " connected.")));
lock (this)
{
lstSockets.Add(handlerSocket);
}
ThreadStart thdsHandler = new ThreadStart(handlerThread);
Thread thdHandler = new Thread(thdsHandler);
thdHandler.Start();
}
}
}
And then lasty here is the handler thread method:
public void handlerThread()
{
try
{
int iBlockSize = 1024 * 3000; //3mb block size
Byte[] dataByte = new Byte[iBlockSize];
Byte[] rcvdData = new Byte[128000 * 1024];//128mb File Limit
Socket handlerSocket = (Socket)lstSockets[lstSockets.Count - 1];
NetworkStream networkStream = new NetworkStream(handlerSocket);
int i = 0;
int iRcvdBytes = 0;
while (true)
{
//Read from socket and store to buffer 'dataByte'
iRcvdBytes = networkStream.Read(dataByte, 0, iBlockSize);
dataByte.CopyTo(rcvdData, i);//Copy recieved bytes,from buffer, to another byte array
i += iRcvdBytes;
if (iRcvdBytes == 0) break;
}
//Get the File name length, BitConvertor occupies the first 4 bytes
int iFileNameLength = BitConverter.ToInt32(rcvdData, 0);
//Get the file name using length as the size and 4 as the offset
string sFileName = Encoding.ASCII.GetString(rcvdData, 4, iFileNameLength);
Stream fileStream = File.Open("C:\\TCPFileServer\\" + sFileName, FileMode.Create);
//Populate raw File on local machine
fileStream.Write(rcvdData, 4 + iFileNameLength, i - 4 - iFileNameLength);
fileStream.Close();
//Update BRS Net Files Server Log
this.Invoke((Action)(() => lstConnections.Items.Add(sFileName + ": Transfered.")));
//Close Connection
networkStream.Close();
handlerSocket = null; //Clear socket
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Now I have debugged and as far I can determine, the first time I can see a problem is when we are trying to determine the file name length where the code reads int iFileNameLength = BitConverter.ToInt32(rcvdData, 0);
- When debugging this variable is always determined as '0', which I assume is not correct? I am not sure. The required results that needs to be achieved is that the Server should receive the file and after the successful transfer, the file name should be displayed in the ListBox.
Here is screen shots showing the problem i'm experiencing:
We can see that bytes are definitely received:
Note that the file name can not be retrieved from using the file name length and offset:
And thus, the following error occurs:
My experience and expertise lie elsewhere and this is the first time I am coding within this paradigm(File transfer over network). I do however have theoretical knowledge about/studied the OSI model and TCP/IP Protocol stack, but never coded anything like this. One note given to me was that the server was coded with the assumption that it will be executed on a specific PC and I am allowed to change code of the Server application if it is absolutely necessary.
Upvotes: 1
Views: 2145
Reputation: 34421
Try this. You have to do thinks in reverse on the client and using a List makes things easier.
private void TransferFile(string _sFileName, string _sIPAdress)
{
List<Byte> bFileBuffer = File.ReadAllBytes(_sFileName).ToList();
byte[] bFileName = Encoding.ASCII.GetBytes(_sFileName);
bFileBuffer.InsertRange(0, bFileName);
//Get the File name length, BitConvertor occupies the first 4 bytes
byte[] brcvdDataCount = BitConverter.GetBytes((UInt32)_sFileName.Count());
bFileBuffer.InsertRange(0, brcvdDataCount);
//Open TCP/IP Connection
TcpClient tcpClientSocket = new TcpClient(_sIPAdress, 8080);
NetworkStream nsNetworkStream = tcpClientSocket.GetStream();
nsNetworkStream.Write(bFileBuffer.ToArray(), 0, bFileBuffer.Count);
nsNetworkStream.Close();
}
Upvotes: 1
Reputation: 34421
There are two things with the server code you should be aware of
/Get the File name length, BitConvertor occupies the first 4 bytes
int iFileNameLength = BitConverter.ToInt32(rcvdData, 0);
//Get the file name using length as the size and 4 as the offset
string sFileName = Encoding.ASCII.GetString(rcvdData, 4, iFileNameLength);
1) You need to add a byte count to beginning of upload. 2) The code is using Ascii Encoding which means you can not upload binary data.
Upvotes: 1