1

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: Received Bytes

Note that the file name can not be retrieved from using the file name length and offset: Blank file name

And thus, the following error occurs: Error

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.

1
  • dataByte.CopyTo(rcvdData, i); is wrong, iRcvdBytes <= iBlockSize, you are treating it as iRcvdBytes == iBlockSize || iRcvdBytes == 0. dataByte has "junk data" at the end of the array you are copying to rcvdData. Commented Sep 6, 2015 at 23:30

2 Answers 2

1

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();

        }​​
Sign up to request clarification or add additional context in comments.

9 Comments

I see, but is there a way to do it without changing the server code? Like how would I use ASCII encoding instead of using bytes when sending the data via the client?
I'm not changing the server code. The server is receiving bytes and I'm sending bytes. The server is expecting the filename length to be the first four bytes, then filename, then data.
Understood, but how do I approach this, how do i do the reverse on the client side? I updated my post to include screen shots of my problem. Sorry i'm really out of my depth with this subject.
Updated code. Put the code in server instead of client.
It needs to be 2 separate programs - I need code in the client to convert and send data and server should receive and decode. The code you supplied above is doing the work of the client and server. I also tried to use the first part of the code that you supplied (List of bytes and adding file name to byte List) in my client and is still doesn't work. Basically the server code is not to be changed(the code I posted originally) and I only have to adapt the client code so that the file name and file data itself can be retrieved and extracted by the server. I only need a fix for the client code
|
1

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.

1 Comment

I see. How would I (1) add the byte count when I'm sending and (2) encode data with ASCII encoding? Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.