Marnus Steyn
Marnus Steyn

Reputation: 1083

Trouble with sending using TCP File Transfer Client

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.

Upvotes: 1

Views: 2145

Answers (2)

jdweng
jdweng

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

jdweng
jdweng

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

Related Questions