3

I have a problem with the Client/Server application i'm writing. It's aim is to send a specific directory's files in the Client and send them to a directory on the Server.

I get the files with a foreach, but when i put a breakpoint at the begin of the foreach and I continue until I send all the files I get all of them in my server as I want, when i delete the breakpoint and re-run my application, my server just receives part of the files, and i don't know why.

I'm unsure but I suppose it's a threading issue, but don't know how to solve it.

Server:

static void Main(string[] args)
{
     try
      {

            TcpListener listen = new TcpListener(3003);
            TcpClient client;
            int bufferSize = 1024;
            NetworkStream netStream = null;
            int bytesRead = 0;
            int allBytesRead = 0;

            // Start listening
            listen.Start();

            // Accept client
            client = listen.AcceptTcpClient();


            StreamReader reader = new StreamReader(client.GetStream());

            netStream = client.GetStream();
            string fileName;
            bool endOfSend=false;
           do
            {
                fileName = reader.ReadLine();

                // Read length of incoming data
                byte[] length = new byte[4];

                bytesRead = netStream.Read(length, 0, 4);
                int dataLength = BitConverter.ToInt32(length, 0);

                // Read the data
                int bytesLeft = dataLength;
                byte[] data = new byte[dataLength];


                    while (bytesLeft > 0)
                    {

                        int nextPacketSize = (bytesLeft > bufferSize) ? bufferSize : bytesLeft;

                        bytesRead = netStream.Read(data, allBytesRead, nextPacketSize);
                        allBytesRead += bytesRead;
                        bytesLeft -= bytesRead;

                    }                  

                allBytesRead = 0;
                bytesLeft = 0;
                bytesRead = 0;

                // Save file to desktop
                Console.WriteLine("File {0} received.", fileName);
                File.WriteAllBytes(@"C:\Users\toto2\Desktop\" + fileName, data);

            } while (!endOfSend);
            netStream.Close();
            client.Close();
            Console.ReadLine();
        }
        catch (Exception ex)
        {
            Console.WriteLine("File Receiving fail." + ex.Message);
        }
    }

Client:

static void Main(string[] args)
{
try
{
    IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
    int port = 3003;

    TcpClient client = new TcpClient();
    //NetworkStream netStream;

    // Connect to server
    try
    {
        client.Connect(new IPEndPoint(ipAddress, port));
        Console.WriteLine("Connecté.....");
        SendFiles(client);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

    // Clean up
    client.Close();
    Console.Read();
}
catch (Exception ex)
{
    Console.WriteLine("File Sending fail." + ex.Message);
}


}

public static  void SendFiles(TcpClient cli) {
    NetworkStream netS=cli.GetStream();

   int bufferSize = 1024;
    string[] files = Directory.GetFiles(@"C:\Users\toto\Mes Images\test");


StreamWriter writer = new StreamWriter(cli.GetStream());


foreach (string item in files)
{
    writer.WriteLine(Path.GetFileName(item));
    writer.Flush();


    // Read bytes from image
    byte[] data = File.ReadAllBytes(Path.GetFullPath(item));

    // Build the package
    byte[] dataLength = BitConverter.GetBytes(data.Length);
    byte[] package = new byte[4 + data.Length];
    dataLength.CopyTo(package, 0);
    data.CopyTo(package, 4);

    // Send to server
    int bytesSent = 0;
    int bytesLeft = package.Length;

    while (bytesLeft > 0)
    {

        int nextPacketSize = (bytesLeft > bufferSize) ? bufferSize : bytesLeft;

        netS.Write(package, bytesSent, nextPacketSize);
        bytesSent += nextPacketSize;
        bytesLeft -= nextPacketSize;

    }
}
writer.Close();
netS.Close();
}

Thank to anyone will try to help me.

aydjay
  • 858
  • 11
  • 25
  • 2
    While extremely unlikely your read to fill the buffer for `dataLength` is not guaranteed to read all 4 bytes. You need to loop for that just like you loop for your main data (I recommend you make a static helper method where you pass in TcpClient and the number of bytes you want and it returns a fully filled `byte[]` of that size. See my `ReadFully` method from [this answer](http://stackoverflow.com/a/29929428/80274), it uses Socket instead of TcpClient but the principal is the same) – Scott Chamberlain May 20 '15 at 15:46

2 Answers2

1

You may want to implement and acknowledgement from the server to the client that it has recieved 1 file. Then instruct the client to send the next file. As far as I can tell you are just sending all the files at once. The following is a simple implementation of the acknowledgement. You should be able to take the relevant parts of it for your scenario.

//
/*   Server Program    */

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;

public class serv {
    public static void Main() {
    try {
        IPAddress ipAd = IPAddress.Parse("172.21.5.99");
         // use local m/c IP address, and 
         // use the same in the client

/* Initializes the Listener */
        TcpListener myList=new TcpListener(ipAd,8001);

/* Start Listeneting at the specified port */        
        myList.Start();

        Console.WriteLine("The server is running at port 8001...");    
        Console.WriteLine("The local End point is  :" + 
                          myList.LocalEndpoint );
        Console.WriteLine("Waiting for a connection.....");

        Socket s=myList.AcceptSocket();
        Console.WriteLine("Connection accepted from " + s.RemoteEndPoint);

        byte[] b=new byte[100];
        int k=s.Receive(b);
        Console.WriteLine("Recieved...");
        for (int i=0;i<k;i++)
            Console.Write(Convert.ToChar(b[i]));

        ASCIIEncoding asen=new ASCIIEncoding();
        s.Send(asen.GetBytes("The string was recieved by the server."));
        Console.WriteLine("\nSent Acknowledgement");
/* clean up */            
        s.Close();
        myList.Stop();

    }
    catch (Exception e) {
        Console.WriteLine("Error..... " + e.StackTrace);
    }    
    }

}

---------------------------------------------------------------------------

/*       Client Program      */

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Net.Sockets;


public class clnt {

    public static void Main() {

        try {
            TcpClient tcpclnt = new TcpClient();
            Console.WriteLine("Connecting.....");

            tcpclnt.Connect("172.21.5.99",8001);
            // use the ipaddress as in the server program

            Console.WriteLine("Connected");
            Console.Write("Enter the string to be transmitted : ");

            String str=Console.ReadLine();
            Stream stm = tcpclnt.GetStream();

            ASCIIEncoding asen= new ASCIIEncoding();
            byte[] ba=asen.GetBytes(str);
            Console.WriteLine("Transmitting.....");

            stm.Write(ba,0,ba.Length);

            byte[] bb=new byte[100];
            int k=stm.Read(bb,0,100);

            for (int i=0;i<k;i++)
                Console.Write(Convert.ToChar(bb[i]));

            tcpclnt.Close();
        }

        catch (Exception e) {
            Console.WriteLine("Error..... " + e.StackTrace);
        }
    }

}
aydjay
  • 858
  • 11
  • 25
1

You've got a hybrid binary/text protocol. That can be painful. StreamReader buffers parts of the stream. It takes more than it returns immediately as a string. You can't mix it with other readers.

Throw all of this code away and use a higher-level communication mechanism. For example WCF with MTOM for binary streaming. Or HTTP.

If you are unwilling to to that use BinaryReader ad BinaryWriter. They are reasonably easy to use.

Note, that when you read the length you assume that all 4 bytes will arrive in one read. That assumption is false.

usr
  • 168,620
  • 35
  • 240
  • 369