2011-08-25 244 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)) –