2012-03-11 14 views
2

我想寫一個小的C#應用​​程序,它將能夠以客戶端/服務器方式發送和接收加密的消息。C#通過NetworkStream發送和接收消息 - 簡單的代碼,但不能按預期工作

這MSDN的例子是非常接近的東西,我需要(只是爲了獲得基本的功能) http://msdn.microsoft.com/en-us/library/as0w18af.aspx http://msdn.microsoft.com/en-us/library/te15te69.aspx

所以客戶端對消息進行加密,將其發送到服務器,服務器將其解密並將明文寫入控制檯。

一切工作正常,在這一個方向

但是當我嘗試發送郵件從服務器到客戶端(如加密或明文無所謂),它失敗。

Server代碼

using System; 
using System.Net.Sockets; 
using System.Threading; 
using System.IO; 
using System.Net; 
using System.Security.Cryptography; 

class Class1 
{ 
    static void Main(string[] args) 
    { 
     try 
     { 
     byte[] Key = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; 
     byte[] IV = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; 

     string ipAddress = "127.0.0.1"; 
     TcpListener TCPListen = new TcpListener(IPAddress.Parse(ipAddress),11000); 

     TCPListen.Start(); 

     while(!TCPListen.Pending()) 
     { 
      Console.WriteLine("Still listening. Will try in 5 seconds."); 
      Thread.Sleep(5000); 
     } 

     TcpClient TCP = TCPListen.AcceptTcpClient(); 

     NetworkStream NetStream = TCP.GetStream(); 


     RijndaelManaged RMCrypto = new RijndaelManaged(); 



     CryptoStream CStream_READ = new CryptoStream(NetStream, RMCrypto.CreateDecryptor(Key, IV), CryptoStreamMode.Read); 
     StreamReader SReader = new StreamReader(CStream_READ); 
     Console.WriteLine("The decrypted original message: {0}", SReader.ReadToEnd()); 

// so far so good, but the next portion of the code does not run properly 

     CryptoStream CStream_WRITE = new CryptoStream(NetStream, RMCrypto.CreateEncryptor(Key,IV),CryptoStreamMode.Write); 
     StreamWriter SWriter = new StreamWriter(CStream_WRITE); 
     SWriter.WriteLine("message from server"); 
     SWriter.Flush(); 
     Console.WriteLine("The message was sent."); 



     SReader.Close(); 
     SWriter.Close(); 
     NetStream.Close(); 
     TCP.Close(); 
     } 
     //Catch any exceptions. 
     catch 
     { 
     Console.WriteLine("The Listener Failed."); 
     } 
    } 
} 

客戶端代碼

using System; 
using System.IO; 
using System.Security.Cryptography; 
using System.Net.Sockets; 

public class main 
{ 
    public static void Main(string[] args) 
    { 
     try 
     { 

     TcpClient TCP = new TcpClient("localhost",11000); 

     NetworkStream NetStream = TCP.GetStream(); 

     RijndaelManaged RMCrypto = new RijndaelManaged(); 

     byte[] Key = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; 
     byte[] IV = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; 

     CryptoStream CStreamWRITE = new CryptoStream(NetStream, RMCrypto.CreateEncryptor(Key, IV), CryptoStreamMode.Write); 

     StreamWriter SWriter = new StreamWriter(CStreamWRITE); 

     SWriter.WriteLine("Hello World!"); 
     SWriter.Flush(); 

     Console.WriteLine("The message was sent."); 

    // so far so good, but the next portion of the code does not run properly 
     CryptoStream CStreamREAD = new CryptoStream(NetStream, RMCrypto.CreateDecryptor(Key,IV),CryptoStreamMode.Read); 
     StreamReader SReader = new StreamReader(CStreamREAD); 
     Console.WriteLine("od servera som dostal: {0}", SReader.ReadToEnd()); 

     SWriter.Close(); 
     SWriter.Close(); 
     CStreamWRITE.Close(); 
     NetStream.Close(); 
     TCP.Close(); 
     } 
     catch 
     { 
     Console.WriteLine("The connection failed."); 
     } 
    } 
} 

我做什麼,我只是反映我認爲相關的執行功能 所以服務器應該是第一個代碼接收來自客戶端的消息,然後發送消息給客戶端

如果我註釋掉客戶端發送消息到服務器,那麼服務器successf ully發送消息給客戶端

請問您能否幫我解決,並告訴我爲什麼它不能同時在兩個方向上工作,但是分開就行了?

在此先感謝

+2

你收到了什麼錯誤? – 2012-03-11 23:21:47

+0

我沒有收到任何錯誤消息。 問題是當服務器發送消息的代碼時,不僅代碼不會執行(而是一步一步地釋放代碼),而且服務器也不會(可能)接收消息。雖然該行執行 當我註釋掉它沒有被寫在控制檯,它解密收到的meesage精細 – Martin 2012-03-12 06:40:38

回答

0

據我所知Read() - 方法上NetworkStream沒有阻止,因此嘗試通過ReadToEnd()讀取發送起始消息不會找到任何數據後,因此不會收到一條消息(以空字符串返回)。在調用讀取方法之前,請嘗試使用.DataAvailable -property等待數據。舉例來說,參見MSDN

代碼中的第二個問題是在寫入CryptoStream之後缺少FlushFinalBlock(),因爲這些流保留它的狀態,否則在關閉之前不發送部分加密塊(請注意,這是一個嚴重的區別在Flush()FlushFinalBlock()之間)。所以你應該在等待閱讀答案之前在客戶端撥打CStreamWRITE.FlushFinalBlock()

+0

謝謝,我試圖與剛剛重命名我的NetworkStream到客戶端添加示例代碼。 它執行do塊的主體並停在那裏。 問題的關鍵在於,當服務器發送消息的代碼時,不僅代碼不會執行(而是一步一步地釋放它),而且服務器也不會(可能)接收消息。沒有任何東西寫在控制檯上,儘管該行被執行 當我將它註釋掉時,它將接收到的消息正確地解密 – Martin 2012-03-12 06:42:23

+0

第二次看,我看到寫完後你不會調用FlushFinalBlock()。這樣服務器將看不到消息(甚至可能根本沒有發送任何消息,因爲消息太短),我更新了答案。 – Stefan 2012-03-12 08:19:42

+0

'Read'方法非常** **阻塞(如果沒有數據)和'.DataAvailable'幾乎總是錯誤的檢查 - 這並不意味着大多數人認爲這意味着什麼 – 2012-03-12 08:23:09

2

要調用ReadToEnd在接收機 - 然而,發件人沒有說它已完成發送 - 發送者僅傳入的消息永遠不會到來後關閉流等;基本上,你已經陷入僵局。

有很多方法可以解決這類問題,其中包括:

  • 使用長度前綴的郵件,以便接收器知道有多少數據預先期待的(並且可以相應地限制本身)
  • 對消息使用終結符(可能是cr/lf),因此接收者知道它已到達消息的末尾;對於文本協議,ReadLine然後可以是有用的
  • 關閉所述出站流

最後是簡單如果只發送一個消息;只需使用:

socket.Shutdown(SocketShutdown.Send); 

在發送消息後,接收方將知道它不會在套接字上獲得更多的數據。還要注意的是,你必須在之前清除諸如密碼流這樣的事情,以確保數據實際上全部被髮送(並且不被緩衝和丟失)。

個人而言,我傾向於使用第一個選項(長度前綴),主要是因爲我通常處理多消息二進制協議。