Client Server文件传输

时间:2013-09-09 13:57:15

标签: c# sockets file-transfer waitone

从C#中的小文件传输套接字应用程序开始(目前主要是MS示例代码)。使用ManualResetEvent.WaitOne()时遇到问题。我在 foreach循环中调用它,所以也许这就是问题所在 在任何情况下,我都试图命令/阻止BeginSend调用,以便在成功文件传输后启动。但似乎即使我在每次BeginSend之后调用WaitOne(),它们也会出现故障。

以下是一个示例:

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

...这里是步骤的顺序:

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

问题是:即使步骤乱序,文件数据在服务器端也完好无损。看起来在它完成之前多次调用BeginSend是可行的,但我仍然可以选择在开始另一次传输之前等待文件进入服务器。

2 个答案:

答案 0 :(得分:0)

如果可以,您应该考虑使用Web服务。在客户端/服务器之间双向发送/流式传输文件都有很好的嵌入式功能。

答案 1 :(得分:0)

ManualResetEvent的行为与名称告诉我们完全相同:必须手动重置!

  

手动重置事件就像门。当事件未发出信号时,等待它的线程将阻塞。当发出事件信号时,释放所有等待线程,并且在调用其Reset方法之前,事件保持信号通知(即,后续等待不会阻塞)。当一个线程必须在其他线程可以继续之前完成一个活动时,手动重置事件很有用。

     

自动重置事件提供对资源的独占访问。如果在没有线程等待时发出自动重置事件的信号,它将保持信号状态,直到线程尝试等待它。该事件释放线程并立即重置,阻塞后续线程。

(资料来源:http://msdn.microsoft.com/en-us/library/system.threading.eventwaithandle.aspx

出于您的目的,AutoResetEvent应该适合。

进一步消耗: 在您的示例中,它等待第一个Set(),然后门打开,所有以下WaitOne()只能在没有人关闭门时通过。