views:

84

answers:

2

I'm exploring MSMQ services, and I wrote a simple console client-server application that sends each of the client's keystrokes to the server. Whenever hit a control character (DEL, ESC, INS, etc) the server understandably throws an error. However, whenever I type a space character, the server receives the packet but doesn't throw an error and doesn't display the space.

Server:

namespace QIM
{
    class Program
    {
        const string QUEUE = @".\Private$\qim";
        static MessageQueue _mq;
        static readonly object _mqLock = new object();
        static XmlSerializer xs;

        static void Main(string[] args)
        {
            lock (_mqLock)
            {
                if (!MessageQueue.Exists(QUEUE))
                    _mq = MessageQueue.Create(QUEUE);
                else
                    _mq = new MessageQueue(QUEUE);
            }
            xs = new XmlSerializer(typeof(string));
            _mq.BeginReceive(new TimeSpan(0, 1, 0), new object(), OnReceive);
            while (Console.ReadKey().Key != ConsoleKey.Escape) { }
        }

        static void OnReceive(IAsyncResult result)
        {
            Message msg;
            lock (_mqLock)
            {
                try
                {
                    msg = _mq.EndReceive(result);
                    Console.Write(".");
                    Console.Write(xs.Deserialize(msg.BodyStream));
                }
                catch (Exception ex)
                {
                    Console.Write(ex);
                }
            }
            _mq.BeginReceive(new TimeSpan(0, 1, 0), new object(), OnReceive);
        }
    }
}

Client:

namespace QIM_Client
{
    class Program
    {
        const string QUEUE = @".\Private$\qim";
        static MessageQueue _mq;

        static void Main(string[] args)
        {
            if (!MessageQueue.Exists(QUEUE))
                _mq = MessageQueue.Create(QUEUE);
            else
                _mq = new MessageQueue(QUEUE);
            ConsoleKeyInfo key = new ConsoleKeyInfo();
            while (key.Key != ConsoleKey.Escape)
            {
                key = Console.ReadKey();
                _mq.Send(key.KeyChar.ToString());
            }
        }
    }
}

Client Input:

Testing, Testing...

Server Output:

.T.e.s.t.i.n.g.,..T.e.s.t.i.n.g......

You'll notice that the space character sends a message, but the character isn't displayed.

+5  A: 

Your issue is not with MSMQ, it's with the XmlSerializer class. See:

var key = Console.ReadKey();

XmlSerializer s = new XmlSerializer(typeof(string));

using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
    s.Serialize(ms, key.KeyChar.ToString());

    ms.Position = 0;

    var foo = (string)s.Deserialize(ms);
}

If you type a space in the console, you'll see that key.KeyChar.ToString() yeilds " ", but foo is equal to "". Because of the default implementation of XmlReader, the XmlSerializer class considers a string of only whitespace to be empty; if the string contains any other characters, both leading and trailing spaces are preserved. The whitespace does get serialized, but deserializing it turns it into an empty string.

Use this instead:

Console.Write(
    s.Deserialize(System.Xml.XmlReader.Create(msg.BodyStream, 
                                          new System.Xml.XmlReaderSettings() 
                                          { 
                                              IgnoreWhitespace = false 
                                          })));
Adam Robinson
+3  A: 

The answer from @Adam is right. The easiest solution is to use BinaryMessageFormatter (it will result in slightly smaller messages anyway).

After you initialize the message queue objects in both the client and the server, set the formatter explicitly:

_mq.Formatter = new BinaryMessageFormatter();

Then in the server, don't try to mess with BodyStream directly. Instead just use Body (which will have already been deserialized by the formatter):

Console.Write(".");
Console.Write(msg.Body);
Erv Walter
+1. I use the `BinaryMessageFormatter` in my current project, but I thought I'd keep the OP's solution in XML.
Adam Robinson
+1 for suggesting the `BinaryMessageFormatter`, I'll look into that, too.
Daniel Rasmussen
Furthermore, `BinaryMessageFormatter` doesn't throw exceptions for control characters.
Daniel Rasmussen