2011-08-25 243 views
3

我需要通過互聯網向計算機發送幾個大文件。因此,我打開了我打算在路由器上使用的端口,並轉發相應的IP地址。無論如何,讓我向您展示我一直在努力實現的目標。這些類對於小文件非常有效,但有時會導致大文件失敗。通過tcp連接發送大文件

這裏是服務器的代碼(這是一個控制檯應用程序)

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

namespace ConsoleApplication20 
{ 

    //server 
    class Program 
    { 
     static Server s; 
     public static void mm() 
     { 
      s = new Server("192.168.0.196"); 
      s.startServer(); 
      Console.Read(); 
     } 

     static void Main(string[] args) 
     { 
      // Thread t = new Thread(new ThreadStart(mm)); 
      // t.Start(); 
      mm(); 
      Console.Read(); 
      s.disconnect(); 
     } 


    } 



    class MyTCP 
    { 
     protected const int MaxChunkSize = 4096; 

     protected Int32 port { get; set; } 
     protected string serverIP { get; set; } 
     protected TcpClient client { get; set; } 
     protected static NetworkStream stream { get; set; } 

     protected void sendData(NetworkStream stream, Byte[] data) 
     { 
      // Send the message to the connected TcpServer. 
      stream.Write(data, 0, data.Length); 
     } 

     protected String receiveData(NetworkStream stream) 
     { 
      // Buffer to store the response bytes. 
      Byte[] data = new Byte[MaxChunkSize]; 

      // String to store the response ASCII representation. 
      String responseData = String.Empty; 

      // Read the first batch of the TcpServer response bytes. 
      Int32 bytes = stream.Read(data, 0, data.Length); 
      responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes); 
      Console.WriteLine("Received: {0}", responseData); 

      return responseData; 
     } 

     protected static Byte[] textToBytes(string text) 
     { 
      return System.Text.Encoding.ASCII.GetBytes(text); 
     } 

     public virtual void disconnect() { } 

     public bool isServerConected { get { return client.Connected; } } 
    } 

    [Serializable] 
    public class FileProperties 
    { 
     public string FileName { get; set; } 
     public string DestPath { get; set; } 
     public double FileSize { get; set; } 

     public FileAttributes fileAttributes { get; set; } 
     public System.Security.AccessControl.FileSecurity FileSecurity { get; set; } 
     public DateTime creationTime { get; set; } 
     public DateTime lastAccessTime { get; set; } 
     public DateTime lastWriteTime { get; set; } 
    } 

    class Server: MyTCP 
    { 
     private System.IO.FileStream _FileStream; 
     private static TcpListener server; 
     private static bool disconect; 

     /// <summary> 
     /// Constructor 
     /// </summary> 
     /// <param name="localAddr">The ip address of the server</param> 
     /// <param name="port">on what port the server going to be listening to?</param> 
     /// <param name="autoStartServer">start listening for connections now? you may call the startserver() method latter...</param> 
     public Server(string localAddr, Int32 port = 13000, bool autoStartServer = false) 
     { 
      this.port = port; 
      this.serverIP = localAddr; 

      if (autoStartServer) 
       start(); 
     } 

     /// <summary> 
     /// Start listening for connections 
     /// </summary> 
     public void startServer() 
     { 
      start(); 
     } 

     public override void disconnect() 
     { 
      // Close everything. 
      stream.Close(); 
      client.Close(); 
      server.Stop(); 
      disconect = true; 
     } 


     void start() 
     { 
      server = null; 

      try 
      { 
       // TcpListener server = new TcpListener(port); 
       server = new TcpListener(IPAddress.Parse(serverIP), port); 

       // Start listening for client requests. 
       server.Start(); 

       // Buffer for reading data 
       Byte[] bytes = new Byte[MaxChunkSize]; 
       String data = null; 

       // Enter the listening loop. 
       while (disconect==false) 
       { 
        Console.Write("Waiting for a connection... "); 

        // Perform a blocking call to accept requests. 
        // You could also user server.AcceptSocket() here. 
        client = server.AcceptTcpClient(); 
        Console.WriteLine("Connected!"); 




        // Get a stream object for reading and writing 
        stream = client.GetStream(); 



        int i; 
        try 
        { 
         // Loop to receive all the data sent by the client. 
         while ((i = stream.Read(bytes, 0, bytes.Length)) != 0) 
         { 


          // Translate data bytes to a ASCII string. 
          data = System.Text.Encoding.ASCII.GetString(bytes, 0, i); 
          Console.WriteLine("Received: {0}", data); 

          if (data.ToUpper().Contains("<sendFile>".ToUpper())) 
          { 
           receiveFile(bytes); 
          } 



          continue; 


         } 
        } 
        catch { } 

        // Shutdown and end connection 
        client.Close(); 
       } 
      } 
      catch (SocketException e) 
      { 
       Console.WriteLine("SocketException: {0}", e); 
      } 
      finally 
      { 
       // Stop listening for new clients. 
       server.Stop(); 
      } 


      Console.WriteLine("\nHit enter to continue..."); 
      Console.Read(); 
     } 


     void receiveFile(Byte[] bytes) 
     { 
      // send 1 
      sendData(stream, textToBytes("<1>")); 

      // receive 2 
      int length = stream.Read(bytes, 0, bytes.Length); 
      byte[] tempA = new byte[length]; 
      for (int k = 0; k < length; k++) 
       tempA[k] = bytes[k]; 

      Stream ms = new MemoryStream(tempA); 
      FileProperties p = new FileProperties(); 
      System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType()); 

      try 
      { 
       p = (FileProperties)x.Deserialize(ms); 

       if (Directory.Exists(p.DestPath)) 
       { 
        //send 3 
        sendData(stream, textToBytes("<3>")); 
       } 
       else 
       { 
        //send 3 
        sendData(stream, textToBytes("<no>")); 
        return; 
       } 
      } 
      catch 
      { 
       //send 3 
       sendData(stream, textToBytes("<no>")); 
       return; 
      } 



      int i; 

      string temp = Path.Combine(new string[]{ p.DestPath, p.FileName + ".temp"}); 

      _FileStream = new System.IO.FileStream(temp, System.IO.FileMode.Create, System.IO.FileAccess.Write); 

       while ((i = stream.Read(bytes, 0, bytes.Length)) != 0) 
       { 
        if (i == 11 & System.Text.Encoding.ASCII.GetString(bytes, 0, i).ToUpper().Equals("</sendFile>".ToUpper())) 
        { 
         _FileStream.Close(); 

         Console.WriteLine("D!"); 

         File.SetAttributes(temp, p.fileAttributes); 
         File.SetAccessControl(temp, p.FileSecurity); 
         File.SetCreationTime(temp, p.creationTime); 
         File.SetLastAccessTime(temp, p.lastAccessTime); 
         File.SetLastWriteTime(temp, p.lastWriteTime); 

         if(File.Exists(temp.Substring(0, temp.Length - 4))) 
          File.Delete(temp.Substring(0, temp.Length - 4)); 

         File.Move(temp, temp.Substring(0, temp.Length - 4)); 


         //sendData(stream, textToBytes("<done>")); 

         Console.WriteLine("Done!"); 

         return; 
        } 
        _FileStream.Write(bytes, 0, i); 

       } 



      return; 

     } 
    } 
} 

和我的客戶端代碼:

using System; 
using System.Net.Sockets; 
using System.Windows; 
using System.IO; 


namespace WpfApplication23sdfd 
{ 

    [Serializable] 
    public class FileProperties 
    { 
     public string FileName { get; set; } 
     public string DestPath { get; set; } 
     public double FileSize { get; set; } 

     public FileAttributes fileAttributes { get; set; } 
     public System.Security.AccessControl.FileSecurity FileSecurity { get; set; } 
     public DateTime creationTime { get; set; } 
     public DateTime lastAccessTime { get; set; } 
     public DateTime lastWriteTime { get; set; } 
    } 

    abstract class MyTCP 
    { 
     protected const int MaxChunkSize = 4096; 

     protected Int32 port { get; set; } 
     protected string serverIP { get; set; } 
     protected TcpClient client { get; set; } 
     protected static NetworkStream stream { get; set; } 

     protected void sendData(NetworkStream stream, Byte[] data) 
     { 

      // Send the message to the connected TcpServer. 
      stream.Write(data, 0, data.Length); 

      // Receive the TcpServer.response. 
     } 

     protected String receiveData(NetworkStream stream) 
     { 
      // Buffer to store the response bytes. 
      Byte[] data = new Byte[MaxChunkSize]; 

      // String to store the response ASCII representation. 
      String responseData = String.Empty; 

      // Read the first batch of the TcpServer response bytes. 
      Int32 bytes = stream.Read(data, 0, data.Length); 
      responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes); 
      Console.WriteLine("Received: {0}", responseData); 

      return responseData; 
     } 

     protected static Byte[] textToBytes(string text) 
     { 
      return System.Text.Encoding.ASCII.GetBytes(text); 
     } 

     public virtual void disconnect() { } 

     public bool isServerConected { get { return client.Connected; } } 
    } 

    //client 
    class Client: MyTCP 
    { 



     /// <summary> 
     /// Constructor 
     /// </summary> 
     /// <param name="serverIP">the ip address of the server</param> 
     /// <param name="port">through what port is the connection going to be established</param> 
     public Client(string serverIP, Int32 port = 13000, bool autoConnect = false) 
     { 
      this.port = port; 
      this.serverIP = serverIP; 

      if (autoConnect) 
       connect(); 
     } 


     public bool connect() 
     { 
      Byte[] data = System.Text.Encoding.ASCII.GetBytes("connect"); 

      // Create a TcpClient. 
      // Note, for this client to work you need to have a TcpServer 
      // connected to the same address as specified by the server, port 
      // combination. 
      try 
      { 
       client = new TcpClient(serverIP, port); 

       // Get a client stream for reading and writing. 
       // Stream stream = client.GetStream(); 
       stream = client.GetStream(); 

       return true; 


      } 
      catch 
      { 
       return false; 
      } 

     } 

     public override void disconnect() 
     { 
      // Close everything. 
      stream.Close(); 
      client.Close(); 
     } 

     static void ConnectOld(String server, Byte[] data) 
     { 
      try 
      { 
       // Create a TcpClient. 
       // Note, for this client to work you need to have a TcpServer 
       // connected to the same address as specified by the server, port 
       // combination. 
       Int32 port = 13000; 
       TcpClient client = new TcpClient(server, port); 



       // Get a client stream for reading and writing. 
       // Stream stream = client.GetStream(); 

       NetworkStream stream = client.GetStream(); 

       // Send the message to the connected TcpServer. 
       stream.Write(data, 0, data.Length); 



       // Receive the TcpServer.response. 

       // Buffer to store the response bytes. 
       data = new Byte[256]; 

       // String to store the response ASCII representation. 
       String responseData = String.Empty; 

       // Read the first batch of the TcpServer response bytes. 
       Int32 bytes = stream.Read(data, 0, data.Length); 
       responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes); 
       Console.WriteLine("Received: {0}", responseData); 

       // Close everything. 
       stream.Close(); 
       client.Close(); 
      } 
      catch (ArgumentNullException e) 
      { 
       Console.WriteLine("ArgumentNullException: {0}", e); 
      } 
      catch (SocketException e) 
      { 
       Console.WriteLine("SocketException: {0}", e); 
      } 

      Console.WriteLine("\n Press Enter to continue..."); 
      Console.Read(); 
     } 

     public void sendFile(string file, string destPath = "c:\\") 
     { 

      //let server know what you are going to be doing... 
      sendData(stream, textToBytes("<sendFile>")); 

      FileProperties p = new FileProperties { 
       creationTime = File.GetCreationTime(file), 
       fileAttributes = File.GetAttributes(file), 
       FileSecurity = File.GetAccessControl(file), 
       lastAccessTime = File.GetLastAccessTime(file), 
       lastWriteTime = File.GetLastWriteTime(file), 
       DestPath = destPath, 
       FileName = Path.GetFileName(file) 
      }; 


      // receive 1 
      if (!receiveData(stream).ToUpper().Contains("<1>".ToUpper())) 
      { 
       MessageBox.Show("Error comunicating with server"); 
       return; 
      } 

      // send object p to server 
      System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(p.GetType()); 
      x.Serialize(stream, p); // send 2 

      //recieve 3 
      if (!receiveData(stream).ToUpper().Contains("<3>".ToUpper())) 
      { 
       MessageBox.Show("Error incorrect parameters sent to server"); 
       return; 
      } 


      System.IO.FileStream streamFile = new System.IO.FileStream(file, System.IO.FileMode.Open, System.IO.FileAccess.Read); 



      while (true) 
      { 
       byte[] chunk = new byte[MaxChunkSize]; 

       int index = 0; 
       // There are various different ways of structuring this bit of code. 
       // Fundamentally we're trying to keep reading in to our chunk until 
       // either we reach the end of the stream, or we've read everything we need. 
       while (index < chunk.Length) 
       { 
        int bytesRead = streamFile.Read(chunk, index, chunk.Length - index); 


        if (bytesRead == 0) 
        { 
         break; 
        } 
        if (bytesRead < MaxChunkSize) 
        { 
         byte[] temp = new byte[bytesRead]; 

         for (var i = 0; i < bytesRead; i++) 
          temp[i] = chunk[i]; 

         chunk = temp; 
        } 


        index += bytesRead; 
       } 
       if (index != 0) // Our previous chunk may have been the last one 
       { 
        sendData(stream,chunk); // index is the number of bytes in the chunk 
       } 
       if (index != chunk.Length) // We didn't read a full chunk: we're done 
       { 

        sendData(stream, textToBytes("</sendFile>".ToUpper())); 

        //receiveData(stream);//wait recall missing to check results 

        return; 
       } 
      } 

     } 

    } 
} 

我實例的方式,客戶端類是通過提供服務器的IP地址爲:

Client c = new Client("192.168.0.196"); 
c.sendFile(@"A:\Users\Tono\Desktop\a.mp4"); 

服務器必須在執行之前先運行該代碼。

我不知道爲什麼使用套接字通過互聯網發送文件非常複雜。我不知道WCF,這就是爲什麼我失去了很多時間來創建這個類。也許已經有一些內置的類可以讓我通過互聯網將文件發送到另一臺計算機。我只知道網絡的基礎知識,如果我可以用簡單的課程來完成,那將會很好。我不明白爲什麼我的課程無法一直工作?如果我增加緩衝區大小,我的類會更有效率嗎?在發送更多字節之前,我是否必須等待或暫停程序?如果有人能告訴我這個班有什麼問題,那將會很好。他們對小文件很好,但對大文件有時不起作用...

回答

8

我可以立即看到幾個問題。可能導致程序只在某些時間工作的原因是,通過TCP發送並不能保證每個send都會在另一側產生相同大小的receive

您的協議似乎認爲它會,因爲您正在等待</sendFile>正好讀取11個字節,而它可以在多個單獨的讀取中接收。例如:「[文件數據...]」。如果發生這種情況,您的代碼將無法正確完成。

還值得注意的是,ASCII編碼是7位,因此二進制文件(如MP4)將被錯誤地接收(即使您修復上述問題)。如果它是二進制數據,則不應嘗試將其轉換爲字符串,而應將其直接從byte[]寫入文件。

如果您希望沿着這條路線(而不是使用已有的許多現有文件傳輸系統,如另一個答案中提到的那樣),那麼您可能還想更改協議,以便不用使用<sendFile>...</sendFile>分隔文件,而是最初發送文件的長度,這將允許您發送可能包含這些特殊標記之一的文件。

+0

非常感謝。你的第二段是我的問題所在。我需要設法讓服務器知道傳輸已完成,以便它可以開始監聽其他命令。此外,我將每個文件分塊,因爲在將大文件放入字節數組時,出現內存錯誤。 –

+0

而我的代碼組織得不好......我不寫ascii字符到我正在轉移的文件:) –

+0

iridum現在可以工作。在發送「」之前,我等了幾秒鐘,並且我還增加了塊大小,並且效果很好。我怎麼知道這個流是否被使用,或者客戶端在發送字節?如果我知道這一點會很好,所以在發送「」之前我不必等待幾秒鐘,而是等待流爲「空閒」,以發送「」並使我的代碼更加高效... –

4

通過套接字發送文件/目錄不是微不足道的。我建議使用一些文件傳輸庫(通過套接字或者更高級別的協議,即rsync,ftp,http等),而不是試圖從頭開始編碼。

在瀏覽代碼之後 - 嘗試發送包含一些統一內容的大文件(填充'0's或其他東西)。如果它會通過 - 你的xml的東西不起作用。

+0

是的,你是對的我會開始尋找一些圖書館。 XML序列化只是節省了我的時間。而不是傳遞幾個字符串,它使我能夠傳遞一個對象。 –

1

基本上你在做什麼是正確的。我可以建議改進的主要觀點是將MaxChunkSize設置爲更大的值,高達65000.這將使套接字代碼能夠處理任何碎片,這比您自己將要執行的任何分割要高效得多。

另外,您知道發送大文件需要一些時間。在100 Mbit LAN中,帶寬(理論上)爲12.5 MByte /秒。因此發送700 MByte文件仍然需要56秒。當然,實際的吞吐量取決於網絡以及涉及的計算機和網絡硬件中的許多因素,因此預期會變慢。

最後,Wireshark(或任何其他嗅探器程序)是網絡程序員工具箱中最基本也是最寶貴的工具。在服務器和客戶端上使用它來查看TCP數據包是如何傳輸的,以查看是否可以確定緩慢傳輸的原因。

4

這是我的代碼發送一個大文件。 一些提示:

  • 檢查您的緩衝區大小。如果太大,它會失敗。
  • 插座標誌。部分標誌效果最好。
  • 由於傳輸時間需要套接字超時。

客戶:

string IPAddress = ""; 
     int Port = 500; 

     string Filename = @"C:\Users\Ben\Desktop\TT.zip"; 


     int bufferSize = 1024; 
     byte[] buffer = null; 
     byte[] header = null; 


     FileStream fs = new FileStream(Filename, FileMode.Open); 
     bool read = true; 

     int bufferCount = Convert.ToInt32(Math.Ceiling((double)fs.Length/(double)bufferSize)); 



     TcpClient tcpClient = new TcpClient(IPAddress, Port); 
     tcpClient.SendTimeout = 600000; 
     tcpClient.ReceiveTimeout = 600000; 

     string headerStr = "Content-length:" + fs.Length.ToString() + "\r\nFilename:" + @"C:\Users\Administrator\Desktop\" + "test.zip\r\n"; 
     header = new byte[bufferSize]; 
     Array.Copy(Encoding.ASCII.GetBytes(headerStr), header, Encoding.ASCII.GetBytes(headerStr).Length); 

     tcpClient.Client.Send(header); 

     for (int i = 0; i < bufferCount; i++) 
     { 
      buffer = new byte[bufferSize]; 
      int size = fs.Read(buffer, 0, bufferSize); 

      tcpClient.Client.Send(buffer,size,SocketFlags.Partial); 

     } 

     tcpClient.Client.Close(); 

     fs.Close(); 

服務器:

int Port = 500; 

     TcpListener listener = new TcpListener(IPAddress.Any, Port); 
     listener.Start(); 


     Socket socket = listener.AcceptSocket(); 

     int bufferSize = 1024; 
     byte[] buffer = null; 
     byte[] header = null; 
     string headerStr = ""; 
     string filename = ""; 
     int filesize = 0; 


     header = new byte[bufferSize]; 

     socket.Receive(header); 

     headerStr = Encoding.ASCII.GetString(header); 


     string[] splitted = headerStr.Split(new string[] { "\r\n" }, StringSplitOptions.None); 
     Dictionary<string, string> headers = new Dictionary<string, string>(); 
     foreach (string s in splitted) 
     { 
      if (s.Contains(":")) 
      { 
       headers.Add(s.Substring(0,s.IndexOf(":")), s.Substring(s.IndexOf(":") + 1)); 
      } 

     } 
     //Get filesize from header 
     filesize = Convert.ToInt32(headers["Content-length"]); 
     //Get filename from header 
     filename = headers["Filename"]; 

     int bufferCount = Convert.ToInt32(Math.Ceiling((double)filesize/(double)bufferSize)); 


     FileStream fs = new FileStream(filename, FileMode.OpenOrCreate); 

     while(filesize > 0) 
     { 
      buffer = new byte[bufferSize]; 

      int size = socket.Receive(buffer,SocketFlags.Partial); 

      fs.Write(buffer,0,size); 

      filesize -= size; 
     } 


     fs.Close(); 

希望這會幫助別人。

+0

+1謝謝你這是非常有用的.. 請注意,您必須刪除尾部「\ 0」之前在服務器中使用它們時收到它們....我用(header.Replace(「\ 0」 0「,string.Empty)) –