Re: Deserialize, CryptoStream, and NetworkStream

From: Stephen Martin (smartin_at_removethis.emsoft.andthis.ca)
Date: 02/27/04


Date: Fri, 27 Feb 2004 13:11:05 -0500

This is normal for a network stream. When you flush the stream that is
writing to the socket all the data is sent to the client. But as the client
is reading the data it has no way of knowing when the data has been
completely received. It just keeps reading the stream until the stream
returns zero bytes, this works fine for a file stream, memory stream, etc.
but a network stream only returns zero bytes when the socket is closed. So,
in your case the client is waiting on a Stream.Read call inside of the
Deserialize method until the socket is closed.

The problem is that TCP is not designed as a message oriented protocol.
Instead of Serializing/Deserializing directly to the stream
Serialize/Encrypt to a buffer and then send the message with the length
prepended so that the client can know when the message is complete without
closing the socket. Or Serialize/Encrypt to a buffer and use UDP.

"Mike" <clarksmr@yahoo.com> wrote in message
news:c1m205$2t0$1@news01.cit.cornell.edu...
> I'm having trouble in C# getting (de)serialization to work over a
> CryptoStream attached to a NetworkStream. The issue is that the
> Deserialize() call blocks until the stream is closed, rather than just
> flushed.
>
> I'd really like to know what I'm doing wrong, so here is a short code
> listing that exhibits the problem. Any insights would be greatly
> appreciated.
>
> -- Mike
>
> Files in listing: client.cs. server.cs
>
> //----------------------------------------------
> // Begin client.cs
> //----------------------------------------------
> using System;
> using System.Net.Sockets;
> using System.Runtime.Serialization.Formatters.Binary;
> using System.Collections;
> using System.IO;
> using System.Security.Cryptography;
> using System.Threading;
>
> class Client
> {
> static void Main(string[] args)
> {
> // Initialize all the objects we need.
> TcpClient client = new TcpClient("localhost", 8000);
> NetworkStream netStream = client.GetStream();
> SymmetricAlgorithm crypt = Rijndael.Create();
> 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};
> crypt.Key = Key;
> crypt.IV = IV;
> BinaryFormatter bf = new BinaryFormatter();
> CryptoStream encryptedStream = new CryptoStream(netStream,
> crypt.CreateEncryptor(), CryptoStreamMode.Write);
> String m = "test";
>
> // Serialize a message to an encrypted network stream, then flush the
> stream
> Console.WriteLine("Serializing message");
> Console.WriteLine("Message is: " + m);
> bf.Serialize(encryptedStream, m);
> Console.WriteLine("Message serialized");
> encryptedStream.FlushFinalBlock();
> encryptedStream.Flush();
> Console.WriteLine("Stream flushed");
>
> // At this point, the message should already have arrived at the server.
> // However, you'll notice during the next 5 seconds that the server
> // will act as though it hasn't received the message.
> Thread.Sleep(5000);
> encryptedStream.Close();
> Console.WriteLine("Stream closed");
> // Now, the server will have displayed the message. So Close() is doing
> // something to finalize sending the message that Flush() didn't do.
>
> // Finally, wait another 5 seconds to prove that Close() is what did it,
> // rather than destruction of some object.
> Thread.Sleep(5000);
> }
> }
> //----------------------------------------------
> // End client.cs
> //----------------------------------------------
>
> //----------------------------------------------
> // Begin server.cs
> //----------------------------------------------
> using System;
> using System.Net;
> using System.Net.Sockets;
> using System.Runtime.Serialization.Formatters.Binary;
> using System.Collections;
> using System.IO;
> using System.Security.Cryptography;
> using System.Threading;
>
> class Server
> {
> static void Main(string[] args)
> {
> IPAddress localHost = Dns.Resolve("localhost").AddressList[0];
> TcpListener server = new TcpListener(localHost, 8000);
> server.Start();
> TcpClient client = server.AcceptTcpClient();
> NetworkStream netStream = client.GetStream();
> SymmetricAlgorithm crypt = Rijndael.Create();
> 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};
> crypt.Key = Key;
> crypt.IV = IV;
> BinaryFormatter bf = new BinaryFormatter();
> CryptoStream encryptedStream = new CryptoStream(netStream,
> crypt.CreateDecryptor(), CryptoStreamMode.Read);
>
> Console.WriteLine("Deserializing message");
> String m = (String) bf.Deserialize(encryptedStream);
> Console.WriteLine("Message deserialized");
> Console.WriteLine("Message was: " + m);
> }
> }
> //----------------------------------------------
> // End server.cs
> //----------------------------------------------
>
>
>
>



Relevant Pages

  • Re: CAsyncSocket Assertion When Terminating
    ... whatever only one client might exist. ... you would have a 'UDP receive socket'. ... I'm just doing the server side - receiving UDP datagrams. ... I use stream flow with CSocket. ...
    (microsoft.public.vc.mfc)
  • Re: How do I stop a Winsock from buffering characters?
    ... it's applied at the OS level to the socket. ... Stream s = client.GetStream; ... from the client code and have the server see it right away. ... first character of the client send. ...
    (microsoft.public.windowsce.embedded)
  • Re: How do I stop a Winsock from buffering characters?
    ... The client is just doing this to send: ... Stream s = client.GetStream; ... I just wan to send one character ... from the client code and have the server see it right away. ...
    (microsoft.public.windowsce.embedded)
  • Re: file transfer with sockets
    ... "Once you have the file on the server side, ... sending the bytes over the stream. ... Of course, your client has to ... then using the socket to send that byte buffer. ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Is this the correct way to send a Bitmap over sockets?
    ... clickpoints) from the server to the client. ... indicating that the Socket was closed. ... "logical" stream ...
    (microsoft.public.dotnet.languages.csharp)