C# send & receive messages via NetworkStream -- simple code but does not work as expected

12,029

You are calling ReadToEnd on the receiver - however, the sender has not said that it has finished sending - the sender only closes the stream etc after the incoming message that will never come; basically, you've deadlocked yourself.

There are many ways to approach this type of problem, including:

  • using a length-prefix to a message so the receiver knows how much data to expect in advance (and can limit itself accordingly)
  • using a terminator to a message (maybe a cr/lf) so the receiver knows it has reached the end of a message; for a text protocol, ReadLine may then be useful
  • closing the outbound stream

the last is simple if you are only sending one message; just use:

socket.Shutdown(SocketShutdown.Send);

after sending the message, and the receiver will know it isn't going to get any more data on the socket. Note also that you must clear down things like the crypto stream before doing this, to ensure the data is actually all sent (and not buffered and lost).

Personally, I tend to use the first option (length prefix), mainly because I'm usually dealing in multi-message binary protocols.

Share:
12,029
Martin
Author by

Martin

I am currently a student. I am interested in data network technologies and currently I have valid CCNA and CCNP SWITCH certification. Since my programming skill is not that good, I will bother you here from time to time. Please bear with me :)

Updated on June 05, 2022

Comments

  • Martin
    Martin almost 2 years

    I am trying to write a small C# application that will be able to send and receive encrypted messages in a client/server manner.

    This MSDN example is pretty close to something I need (just to get the basic functionality) http://msdn.microsoft.com/en-us/library/as0w18af.aspx http://msdn.microsoft.com/en-us/library/te15te69.aspx

    so the client encrypts the message, sends it to the server, the server decrypts it and writes the cleartext to console.

    everything works fine, in this one direction

    however when I try to send the message (does not matter if encrypted or cleartext) from the server to the client, it fails.

    Server code

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

    Client code

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

    What I did is that I just mirrored the code I think is relevant to perform the function so the server is supposed to 1st receive the message from the client and then send message to the client

    if I comment out the client sending the message to the server, then server successfully sends the message to the client

    could you please help me out and tell me why it does not work in both directions at one time but separately it is OK?

    Thanks in advance