Client Server file transfer

2.8k views Asked by At

Starting with a small file transfer socket app in C# (mostly MS sample code for now). Having trouble with ManualResetEvent.WaitOne(). I'm calling it inside foreach loop so maybe that's the problem.
In any case I'm trying to order/block the BeginSend calls, so that they start after successful file transfer. But it seems that even though I call WaitOne() after each BeginSend they get out of order.

Here is a sample:

public class AsynchronousClient {

// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone = 
    new ManualResetEvent(false);
private static ManualResetEvent sendDone = 
    new ManualResetEvent(false);
private static ManualResetEvent receiveDone = 
    new ManualResetEvent(false);

...

private static void SendAll(Socket client, String path)
{
    String[] files = GetFiles(path);

    int i = 0;
    foreach(String file in files){
        Console.WriteLine("STEP 1 - Files left: {0}", files.Length - i);
        SendFile(client, Path.GetFullPath(file), files.Length - i);
        sendDone.WaitOne();        

        i++;
    }
}

private static void SendFile(Socket client, String path, int FilesLeft )
{
    byte[] filesLeft = BitConverter.GetBytes((Int64)FilesLeft);
    byte[] fileData = File.ReadAllBytes(path);
    byte[] fileLength = BitConverter.GetBytes((Int64) fileData.Length);
    byte[] fileMD5;

    using (MD5 md5Hash = MD5.Create())
    {
        fileMD5 = md5Hash.ComputeHash(fileData);
    }

    byte[] filepathdata = Encoding.Unicode.GetBytes(path);
    byte[] filepathLength = BitConverter.GetBytes((Int16)filepathdata.Length);

    byte[] byteData = filesLeft.Concat(fileLength).Concat(fileMD5).Concat(filepathLength).Concat(filepathdata).Concat(fileData).ToArray();

    Console.WriteLine("STEP 2 - File length: {0}", fileData.Length);

    // Begin sending the data to the remote device.
    client.BeginSend(byteData, 0, byteData.Length, 0,
        new AsyncCallback(SendCallback), client);
}

private static void SendCallback(IAsyncResult ar)
{
    try
    {
        // Retrieve the socket from the state object.
        Socket client = (Socket)ar.AsyncState;

        // Complete sending the data to the remote device.
        int bytesSent = client.EndSend(ar);
        Console.WriteLine("STEP 3 - Sent {0} bytes to server.", bytesSent);

        // Signal that all bytes have been sent.
        sendDone.Set();
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
}

...and here is the (out of) order of steps:

STEP 1 - Files left: 16
STEP 2 - File length: 432759
STEP 3 - Sent 433033 bytes to server.
STEP 1 - Files left: 15
STEP 2 - File length: 262623
STEP 1 - Files left: 14
STEP 3 - Sent 262897 bytes to server.
STEP 2 - File length: 459683
STEP 1 - Files left: 13
STEP 2 - File length: 369381
STEP 1 - Files left: 12
STEP 2 - File length: 271126
STEP 1 - Files left: 11
STEP 3 - Sent 459957 bytes to server.
STEP 3 - Sent 369679 bytes to server.
STEP 2 - File length: 1647983
STEP 1 - Files left: 10
STEP 2 - File length: 24761
STEP 1 - Files left: 9
STEP 3 - Sent 25049 bytes to server.
STEP 3 - Sent 271424 bytes to server.
STEP 2 - File length: 858717
STEP 1 - Files left: 8
STEP 2 - File length: 214031
STEP 1 - Files left: 7
STEP 2 - File length: 531963
STEP 1 - Files left: 6
STEP 2 - File length: 227950
STEP 1 - Files left: 5
STEP 2 - File length: 394068
STEP 1 - Files left: 4
STEP 2 - File length: 243546
STEP 1 - Files left: 3
STEP 2 - File length: 173656
STEP 1 - Files left: 2
STEP 2 - File length: 712417
STEP 1 - Files left: 1
STEP 3 - Sent 1648279 bytes to server.
STEP 2 - File length: 1631924
STEP 3 - Sent 859001 bytes to server.
STEP 3 - Sent 214309 bytes to server.
STEP 3 - Sent 532239 bytes to server.
STEP 3 - Sent 228226 bytes to server.
STEP 3 - Sent 394346 bytes to server.
STEP 3 - Sent 243822 bytes to server.
STEP 3 - Sent 173936 bytes to server.
STEP 3 - Sent 712701 bytes to server.
STEP 3 - Sent 1632220 bytes to server.
ReceiveCallback: bytesRead <= 0
Response received : OK

The thing is: even though the steps are out of order, the file data is intact on the server side. It seems that it's viable to call BeginSend multiple times before it finishes, but I would still rather have the option to wait for the file to get through to the server before starting another transfer.

2

There are 2 answers

0
Roland Bär On BEST ANSWER

ManualResetEvent behaves exactly as the name tells us: It has to be reset manually!

Manual reset events are like gates. When the event is not signaled, threads that wait on it will block. When the event is signaled, all waiting threads are released, and the event remains signaled (that is, subsequent waits do not block) until its Reset method is called. Manual reset events are useful when one thread must complete an activity before other threads can proceed.

Automatic reset events provide exclusive access to a resource. If an automatic reset event is signaled when no threads are waiting, it remains signaled until a thread attempts to wait on it. The event releases the thread and immediately resets, blocking subsequent threads.

(Source: http://msdn.microsoft.com/en-us/library/system.threading.eventwaithandle.aspx)

For your purpose, the AutoResetEvent should fit.

Further Explenation: In your example, it waits for the first Set(), then the gate is open and all the following WaitOne() can just pass as nobody closes the gate.

0
Franck On

If you can, you should think about using a web service. There is very good embedded features for sending / streaming file between client/server both ways.