Search code examples
c#serializationstreamdeserializationnamed-pipes

How to send multiple objects using named pipes?


I'm trying to send 4 parameters - one integer, one bool and two strings from server to client using named pipes. I've tried different ways, but still not succeeded. First way - I just converted all parameters to string and tried to send like that, but on client I received all parameters as null:

Server code:

   static void StartServer()
        {
            var server = new NamedPipeServerStream("PipesEnroll", PipeDirection.InOut);

                while (true)
                {
                    server.WaitForConnection();
                    StreamWriter writer = new StreamWriter(server);
                    string terminalTemplate;
                    string matcherTemplate;
                    int mathVersionNumber = 9;
                    int numberFingers;
                    bool isOk = Enroll.EnrollWithoutWCF(retrievedList, mathVersionNumber, out terminalTemplate, out matcherTemplate, out numberFingers);

                    writer.WriteLine(isOk.ToString());
                    writer.WriteLine(terminalTemplate);
                    writer.WriteLine(matcherTemplate);
                    writer.WriteLine(numberFingers.ToString());
                    writer.Flush();

                    server.Disconnect();
           }

Client code:

                   using (var client = new NamedPipeClientStream(".", "PipesEnroll", PipeDirection.InOut))
                    {
                        client.Connect();
                        StreamReader reader = new StreamReader(client);

                          bool isOK = Convert.ToBoolean(reader.ReadLine());
                          string terminalTemplate = reader.ReadLine();
                          string matcherTemplate = reader.ReadLine();
                          int numberFingers = Convert.ToInt32(reader.ReadLine());

                    }

Second way I did is creating list of strings and serialized it on server, deserialized on client using BinaryFormatter, but got this exception:"System.Runtime.Serialization.SerializationException: End of Stream encountered before parsing was completed"

Server code:

   var server = new NamedPipeServerStream("PipesEnroll", PipeDirection.InOut);
while (true)
{
        server.WaitForConnection();
        StreamWriter writer = new StreamWriter(server);   
List<string> sendList = new List<string>();
  sendList.Add(isOk.ToString());
  sendList.Add(terminalTemplate);
  sendList.Add(matcherTemplate);
  sendList.Add(numberFingers.ToString());
  BinaryFormatter formatterSerialize = new BinaryFormatter();
  formatterSerialize.Serialize(writer.BaseStream, sendList);
  writer.Flush();
  server.Disconnect();
 }

Client code:

using (var client = new NamedPipeClientStream(".", "PipesEnroll", PipeDirection.InOut))
          {
             client.Connect();
             StreamReader reader = new StreamReader(client);   
 BinaryFormatter formatterDeserialize = new BinaryFormatter();
    List<string> retrievedList =  (List<string>) formatterDeserialize.Deserialize(reader.BaseStream);
}

Solution

  • Finally I've been able to perform this, using xml serialization and particular data protocol for reading and writing strings.

    Class with multiply data that we need pass from server to client, implements two methods for serialization/deserialization into/from xml:

    [Serializable]
    public class ServerData
    {
        public bool Result { get; set; }
    
        public int Int1 { get; set; }
    
        public string Str1 { get; set; }
    
        public string Str2 { get; set; }
    
        public static string Serialize(ServerData dto)
        {
            //Add an empty namespace and empty value
            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            ns.Add("", "");
            XmlSerializer ser = new XmlSerializer(typeof(ServerData));
            using (StringWriter textWriter = new StringWriter())
            {
                ser.Serialize(textWriter, dto, ns);
                return textWriter.ToString();
            }
        }
    
        public static ServerData Deserialize(string xml)
        {
            XmlSerializer ser = new XmlSerializer(typeof(ServerData));
            using (var reader = new StringReader(xml))
            {
                return (ServerData)ser.Deserialize(reader);
            }
        }
    }
    

    Class helper that defines the data protocol for reading and writing strings:

    public class StreamString
    {
        private Stream ioStream;
        private UnicodeEncoding streamEncoding;
    
        public StreamString(Stream ioStream)
        {
            this.ioStream = ioStream;
            streamEncoding = new UnicodeEncoding();
        }
    
        public string ReadString()
        {
            byte[] strSizeArr = new byte[sizeof(int)];
            ioStream.Read(strSizeArr, 0, sizeof(int));
            int strSize = BitConverter.ToInt32(strSizeArr, 0);
            byte[] inBuffer = new byte[strSize];
            ioStream.Read(inBuffer, 0, strSize);
            return streamEncoding.GetString(inBuffer);
        }
    
        public int WriteString(string outString)
        {
            byte[] outBuffer = streamEncoding.GetBytes(outString);
            byte[] strSize = BitConverter.GetBytes(outBuffer.Length);
            ioStream.Write(strSize, 0, strSize.Length);
            ioStream.Write(outBuffer, 0, outBuffer.Length);
            ioStream.Flush();
            return outBuffer.Length + 2;
        }
    }
    

    Server code:

    [STAThread]
    static void Main(string[] args)
    {
    
        Thread serverThread = new Thread(ServerThread);
        serverThread.Priority = ThreadPriority.Highest;
        serverThread.Start();
        serverThread.Join();
    }
    
    static void ServerThread()
    {
        using (var server = new NamedPipeServerStream("PipesEnroll", PipeDirection.InOut, 1))
        {
            server.WaitForConnection();
            var ss = new StreamString(server);
    
            string terminalTemplate;
            string matcherTemplate;
            const int mathVersionNumber = 9;
            int numberFingers;
            bool isOk = Enroll.EnrollWithoutWCF(images, mathVersionNumber, out terminalTemplate, out matcherTemplate,
                out numberFingers);
            var dtoSend = new ServerData();
            dtoSend.Result = isOk;
            dtoSend.Int1 = numberFingers;
            dtoSend.Str1 = terminalTemplate;
            dtoSend.Str2 = matcherTemplate;
            var xmlSend = ServerData.Serialize(dtoSend);
            ss.WriteString(xmlSend);
            server.WaitForPipeDrain();
            server.Close();
        }
    }
    

    Client code:

     using (
       var client = new NamedPipeClientStream(".", "PipesEnroll", PipeDirection.InOut,
                    PipeOptions.None, TokenImpersonationLevel.Impersonation))
                    {
                     client.Connect();
                     var ss = new StreamString(client);
                     string xmlReceive = ss.ReadString();
                     var dtoReceived = ServerData.Deserialize(xmlReceive);
                     bool isOK = dtoReceived.Result;
                    string terminalTemplate = dtoReceived.Str1;
                     string matcherTemplate = dtoReceived.Str2;
                     int numberFingers = dtoReceived.Int1;
        }
    }