C#send&通过NetworkStream接收消息 - 简单的代码但不能按预期工作

时间:2012-03-11 23:16:51

标签: c# client networkstream tcplistener

我正在尝试编写一个小型C#应用程序,它能够以客户端/服务器方式发送和接收加密消息。

这个MSDN示例非常接近我需要的东西(只是为了获得基本功能) http://msdn.microsoft.com/en-us/library/as0w18af.aspx http://msdn.microsoft.com/en-us/library/te15te69.aspx

因此客户端对消息进行加密,将其发送到服务器,服务器将其解密并将明文写入控制台。

在这个方向上一切正常

然而,当我尝试从服务器向客户端发送消息(无论是加密还是明文)时,它都会失败。

服务器代码

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.");
      }
   }
}

我所做的是我只是镜像了我认为与执行该功能相关的代码 所以服务器应该首先从客户端接收消息,然后向客户端发送消息

如果我注释掉客户端将消息发送到服务器,则服务器会成功将消息发送到客户端

你可以帮我解决一下,告诉我为什么它不能同时在两个方向都起作用,但是另外它可以吗?

提前致谢

2 个答案:

答案 0 :(得分:2)

您正在接收方拨打ReadToEnd - 但是,发件人尚未说它已完成发送 - 发件人仅关闭流 传入的信息永远不会到来;基本上,你已经陷入僵局。

有很多方法可以解决这类问题,包括:

  • 对消息使用长度前缀,以便接收方提前知道预期有多少数据(并且可以相应地限制自己)
  • 使用终结符消息(可能是cr / lf),以便接收者知道它已到达消息的末尾;对于文本协议,ReadLine可能会有用
  • 关闭出站流

如果您只发送一条消息,则最后一步很简单;只需使用:

socket.Shutdown(SocketShutdown.Send);

发送消息后,接收方将知道它不会再获得套接字上的数据。另请注意,您必须清除之前加密流之类的内容,以确保数据实际上全部已发送(并且不会缓冲和丢失)。

就个人而言,我倾向于使用第一个选项(长度前缀),主要是因为我通常处理多消息二进制协议。

答案 1 :(得分:0)

据我所知Read()上的NetworkStream - 方法没有阻塞,因此在发送启动消息后立即尝试通过ReadToEnd()读取将无法找到任何数据,因此没有收到消息(返回一个空字符串)。尝试使用.DataAvailable - 属性在调用读取方法之前等待数据。有关示例,请参阅MSDN

代码中的第二个问题是写入CryptoStream后缺少FlushFinalBlock(),因为流保留了状态,否则在关闭之前不发送部分加密块(注意这是Flush()FlushFinalBlock()之间存在严重差异。因此,在等待阅读答案之前,您应该在客户端上调用CStreamWRITE.FlushFinalBlock()