2011-03-24 22 views
3

我有一個使用.Net遠程處理進行文件傳輸的大型應用程序。在某些情況下,插槽被強制關閉 - 這是因爲我沒有直接使用套接字,而是使用字節數組進行.Net遠程調用(我沒有在一次傳輸中發送整個文件,我正在分裂它)。使用套接字和多個客戶端進行文件傳輸

所以,我決定改變使用套接字實際的文件傳輸部分。

由於概念證明,看看我的原則吧,我寫了一個簡單的控制檯客戶端和服務器。

我使用ASYNCH臨危但同步寫 - 我試圖與這兩個是異步但同樣reseult,並保持同步國產調試更加方便。

什麼應用程序做(下面的代碼)是服務器坐着要傳輸的文件等待,它在給定的名字將它們存儲在目錄中。

當進入被按下,然後服務器讀取收到這些文件,並將它們發送回誰把它們存儲在不同的名稱的客戶端。我想以兩種方式測試文件傳輸。

使用的客戶端應用程序的一個實例,一切都很好 - 服務器臨危它,然後將其發送回客戶端。一切都很好。是的,當你終止服務器時,客戶端會拋出異常 - 但這很好 - 我知道套接字被強行關閉了......我可以在代碼正常工作時處理這些代碼。但是,當我創建客戶端代碼的2個實例(不要忘記稍微修改代碼以讀取要發送的不同文件,並且以不同名稱存儲接收到的文件)時,服務器將從以下位置接收這兩個文件:客戶端,發送第一個回來就好了,然後幾個段到第二個文件拋出與「非阻塞套接字操作無法立即完成」 - 這很奇怪,因爲沒有阻塞,並且收件人是異步 - 而發送實際上是阻止!

任何建議,請給我在做什麼錯了 - 毫無疑問,這是愚蠢的東西,但仍...

最終代碼的目的是爲了能夠有N個客戶端與服務器聯繫併發送文件,並且隨機服務器會將一個或多個文件發送回某些/所有客戶端。

乾杯人!

Server代碼

using System; 
using System.Collections.Generic; 
using System.Net; 
using System.Net.Sockets; 
using System.IO; 

namespace SocketServer 
{ 

    class ConnectionInfo 
    { 
     public Socket Socket; 
     public byte[] Buffer; 
     public int client;   
    } 



    class Program 
    { 

     static int chunkSize = 16 * 1024; 
     static int chucksizeWithoutHeaderData = chunkSize - 8; 
     static List<ConnectionInfo> list = new List<ConnectionInfo>(); 

     static Socket serverSocket; 

     static int nClient = 0; 


     static void AcceptCallback(IAsyncResult result) 
     { 


      ConnectionInfo info = new ConnectionInfo(); 
      info.Socket = serverSocket.EndAccept(result); 

      info.Buffer = new byte[chunkSize]; 


      Console.WriteLine("Client connected"); 
      nClient++; 
      info.client = nClient; 

      list.Add(info); 

      info.Socket.BeginReceive(info.Buffer,0,info.Buffer.Length,SocketFlags.None, new AsyncCallback(ReceiveCallBack), info); 

      serverSocket.BeginAccept(new AsyncCallback(AcceptCallback),null); 

     } 

     static void ReceiveCallBack(IAsyncResult result) 
     { 
      ConnectionInfo info = result.AsyncState as ConnectionInfo; 
      try 
      { 

       Int32 nSegmentNumber = BitConverter.ToInt32(info.Buffer,0); 
       Int32 nMaxSegment = BitConverter.ToInt32(info.Buffer,4); 

       string strFileName = string.Format(@"c:\temp\from-client-{0}.dat",info.client); 

       int bySize = info.Socket.EndReceive(result); 
       using (FileStream fs = new FileStream(strFileName, FileMode.OpenOrCreate)) 
       { 
        Console.WriteLine("Received segment {0} of {1} from client {2}", nSegmentNumber, nMaxSegment, info.client); 
        fs.Position = fs.Length; 
        fs.Write(info.Buffer, 8, bySize-8); 

        if (nSegmentNumber >= nMaxSegment) 
        { 
         Console.WriteLine("Completed receipt from client {0}", info.client); 
        } 

       } 

       info.Socket.BeginReceive(info.Buffer, 0, info.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), info); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex); 
      } 
     } 



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

       Console.WriteLine("Server"); 

       IPAddress address = IPAddress.Parse("127.0.0.1"); //The IP address of the server 
       IPEndPoint myEndPoint = new IPEndPoint(address, 6503); 
       serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

       serverSocket.Bind(myEndPoint); 

       serverSocket.Listen(1000); 

       for (int n = 0; n < 10; ++n) 
       { 
        serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); 
       } 

       Console.WriteLine("Server now waiting"); 
       Console.ReadLine(); 

       foreach (ConnectionInfo info in list) 
       { 


        string strFileName = string.Format(@"c:\temp\from-client-{0}.dat", info.client); 

        using (FileStream fs = new FileStream(strFileName, FileMode.Open, FileAccess.Read, FileShare.None)) 
        { 
         int nMaxChunk = 0; 
         int nCurrentChunk = 0; 

         nMaxChunk = (int)(fs.Length/chucksizeWithoutHeaderData); 
         if ((nMaxChunk * chucksizeWithoutHeaderData) < fs.Length) 
         { 
          ++nMaxChunk; 
         } 


         using (BinaryReader br = new BinaryReader(fs)) 
         { 
          byte[] byBuffer; 
          Int64 nAmount = 0; 
          byte[] byMaxChunk = BitConverter.GetBytes(nMaxChunk); 
          while (fs.Length > nAmount) 
          { 
           ++nCurrentChunk; 

           byte[] byCurrentChunk = BitConverter.GetBytes(nCurrentChunk); 

           byBuffer = br.ReadBytes(chucksizeWithoutHeaderData); 


           Console.WriteLine("Sending {0}bytes, chunk {1} of {2} to client {3}", byBuffer.Length,nCurrentChunk,nMaxChunk, info.client); 

           byte [] byTransmitBuffer = new byte[byBuffer.Length + 8]; 
           Array.Copy(byCurrentChunk, byTransmitBuffer, 4); 
           Array.Copy(byMaxChunk, 0,byTransmitBuffer, 4, 4); 
           Array.Copy(byBuffer, 0, byTransmitBuffer, 8, byBuffer.Length); 

           info.Socket.Send(byTransmitBuffer); 
           nAmount += byBuffer.Length; 
          } 
         } 
        } 
       } 


       Console.WriteLine("Press enter to end server"); 
       Console.ReadLine(); 



      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex); 
       Console.ReadLine(); 
      } 
     } 

    } 
} 

客戶端代碼

using System; 
using System.Collections.Generic; 
using System.Net.Sockets; 
using System.IO; 
using System.Threading; 

namespace SocketClient 
{ 
    class Program 
    { 
     static TcpClient socket = new TcpClient(); 
     static int chunkSize = 16 * 1024; 
     static int chucksizeWithoutHeaderData = chunkSize - 8; 
     static byte[] byReceiveBuffer = new byte[chunkSize]; 
     static void ReceiveCallBack(IAsyncResult result) 
     { 
      Socket socket = result.AsyncState as Socket; 
      try 
      { 
       int bySize = socket.EndReceive(result); 

       Console.WriteLine("Recieved bytes {0}", bySize); 

       if (bySize != 0) 
       { 





        Int32 nSegmentNumber = BitConverter.ToInt32(byReceiveBuffer, 0); 
        Int32 nMaxSegment = BitConverter.ToInt32(byReceiveBuffer, 4); 

        Console.WriteLine("Received segment {0} of {1}", nSegmentNumber, nMaxSegment); 

        string strFileName = string.Format(@"c:\temp\client-from-server.dat"); 
        using (FileStream fs = new FileStream(strFileName, FileMode.OpenOrCreate)) 
        { 
         fs.Position = fs.Length; 
         fs.Write(byReceiveBuffer, 8, bySize-8); 
        } 

        if (nSegmentNumber >= nMaxSegment) 
        { 
         Console.WriteLine("all done"); 
        } 
       } 


       socket.BeginReceive(byReceiveBuffer, 0, byReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex); 
      } 
     } 



     static void Main(string[] args) 
     { 
      Console.WriteLine("Press enter to go"); 
      Console.ReadLine(); 


      socket.Connect("127.0.0.1", 6503); 

      Console.WriteLine("Client"); 
      Console.ReadLine(); 

      byte[] byBuffer; 




      socket.Client.BeginReceive(byReceiveBuffer, 0, byReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket.Client); 


      using (FileStream fs = new FileStream(@"c:\temp\filetosend.jpg", FileMode.Open, FileAccess.Read, FileShare.None)) 
      { 



       using (BinaryReader br = new BinaryReader(fs)) 
       { 

        int nMaxChunk = 0; 
        int nCurrentChunk = 0; 

        nMaxChunk = (int)(fs.Length/chucksizeWithoutHeaderData); 
        if ((nMaxChunk * chucksizeWithoutHeaderData) < fs.Length) 
        { 
         ++nMaxChunk; 
        } 

        byte[] byMaxChunk = BitConverter.GetBytes(nMaxChunk); 

         Int64 nAmount = 0; 

         while (fs.Length > nAmount) 
         { 
          ++nCurrentChunk; 
          byte[] byCurrentChunk = BitConverter.GetBytes(nCurrentChunk); 

          byBuffer = br.ReadBytes(chucksizeWithoutHeaderData); 
          Console.WriteLine("Sending {0}bytes, chunk {1} of {2}", byBuffer.Length, nCurrentChunk, nMaxChunk); 

          byte[] byTransmitBuffer = new byte[byBuffer.Length + 8]; 
          Array.Copy(byCurrentChunk, byTransmitBuffer, 4); 
          Array.Copy(byMaxChunk, 0, byTransmitBuffer, 4, 4); 
          Array.Copy(byBuffer, 0, byTransmitBuffer, 8, byBuffer.Length); 

          socket.Client.Send(byTransmitBuffer); 
          nAmount += byBuffer.Length; 
         } 


       } 
      } 



      Console.WriteLine("done"); 


      Console.ReadLine(); 



     } 
    } 
} 

回答

0

我想我可能有一個解決方案 - 雖然我不是100%自信......如果失敗,我會繼續測試並報告回來。

無論如何 - 我已經設置了socket.SendBufferSize和RecieveBufferSize爲4 x的塊大小,現在看起來都很好。

那裏的東西(re緩衝區大小)只是把問題關閉,因爲我認爲它可能。

但是,我在別處遇到了代碼段,並將這些代碼放入代碼中解決了問題。

try 
          { 
           bySent = info.Socket.Send(byTransmitBuffer); 
          } 
          catch (SocketException ex) 
          { 
           Console.WriteLine("Only sent {0}, remaining = {1}", bySent,fs.Length -nAmount); 
           if (ex.SocketErrorCode == SocketError.WouldBlock || 
            ex.SocketErrorCode == SocketError.IOPending || 
            ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable) 
           { 
            // socket buffer is probably full, wait and try again 
            Thread.Sleep(30); 
           } 
           else 
            throw ex; // any serious error occurr 
          } 
0

既然你modifyng的基礎設施,在C#中使用FTP庫,並安裝一些免費的FTP服務器(即FileZilla或其他人)。您可以輕鬆使用一些FTP庫,例如this one,這是可靠的(我在生產代碼中使用它)。

+0

嗨,謝謝你的建議。但這不是一個選項 - 這需要一個獨立的服務,只需一個安裝程序。當我說「這個」時,我的意思是這是一個PoC的程序。有關上述代碼可能出錯的任何建議? – Manicguitarist 2011-03-24 18:58:29

+0

也 - 也需要在所有的客戶端上安裝FTP服務器....並要求戳穿防火牆漏洞......以及缺少加密和...並且... – Manicguitarist 2011-03-24 20:01:39

0

套接字編程可能會很棘手。如果您發送100個字節,這並不意味着您將在服務器中收到100個字節。你可以在多個數據包中接收這100個字節,你必須添加代碼來控制它。

我說,因爲這條線在你的服務器代碼:

fs.Write(info.Buffer, 8, bySize-8); 

我們假定你是,你會收到至少8位,它可以是錯誤的。 bySize可以小於您的塊大小(如果連接已被客戶端關閉,則可以爲零,如果出現錯誤,則可以爲< 0)。

關於您的錯誤,我測試了您的代碼,我可以複製您的代碼問題:

  • 啓動服務器
  • 啓動客戶端和傳輸文件
  • 退出客戶端按enter
  • 服務器崩潰

服務器崩潰,因爲套接字在所有文件傳輸後都在等待更多數據。客戶關閉它。

我解決關閉在客戶端的連接被髮送的文件之後問題:

socket.Client.Disconnect(false); 

然後服務器接收bySize = 0字節,這意味着連接被關閉。在服務器我換成這樣的:

int bySize = socket.EndReceive(result); 

此:

int bySize = 0; 
try 
{ 
    bySize = info.Socket.EndReceive(result); 
} 
catch (Exception ex) 
{ 
    Console.WriteLine("Error from client {0}: {1}", info.client, ex.Message); 
    return; 
} 

if (bySize <= 0) 
    return; 

看一看這裏:http://msdn.microsoft.com/en-us/library/5w7b7x5f.aspx#Y240

這裏:http://msdn.microsoft.com/en-us/library/fx6588te.aspx

編輯:我忘了提及這一點,你只需要調用BeginAccept一次。我刪除了for語句。

// for (int n = 0; n < 10; ++n) 
// { 
    serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); 
// } 
+0

缺少關閉的問題套接字實際上並不是我要發佈的問題。我知道如何關閉套接字 - 但對於PoC代碼,我並不擔心最終化錯誤 - 當服務器嘗試將文件發送回第二個客戶端時,錯誤更多。同樣的問題與發送的數據量 - 對於我的測試,我很高興它的工作方式 - 這就是爲什麼我打印出大量的數據收到,以便我可以打折作爲問題的來源 - 處理不同數據包大小的可能性會使得PoC代碼更復雜 – Manicguitarist 2011-03-24 20:03:13

+0

重新使用10x BeginAccepts,即一次允許10個通道(如果需要)。對於PoC代碼,您是正確的,即使在同一時間連接兩個客戶端的可能性也很小。 – Manicguitarist 2011-03-24 20:07:29

相關問題