如何使用命名管道发送多个对象

本文关键字:对象 管道 何使用 | 更新日期: 2023-09-27 18:27:22

我正在尝试使用命名管道从服务器向客户端发送4个参数——一个整数、一个布尔和两个字符串。我尝试过不同的方法,但仍然没有成功。第一种方式-我只是将所有参数转换为字符串,并尝试像那样发送,但在客户端上,我收到了所有参数为null:

服务器代码:

   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();
           }

客户代码:

                   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());
                    }

我做的第二种方法是创建字符串列表并在服务器上对其进行序列化,在客户端上使用BinaryFormatter进行反序列化,但得到了以下异常:"System.Runtime.Serialization.Serialization异常:在分析完成前遇到流的末尾"

服务器代码:

   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();
 }

客户代码:

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

如何使用命名管道发送多个对象

最后,我能够执行此操作,使用xml序列化和特定的数据协议来读取和写入字符串。

使用我们需要从服务器传递到客户端的乘法数据类,实现了两种序列化/反序列化到xml/从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);
        }
    }
}

定义用于读取和写入字符串的数据协议的类助手:

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;
    }
}

服务器代码:

[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();
    }
}

客户代码:

 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;
    }
}

有一种方法可以做到这一点,而无需序列化/反序列化。序列化/反序列化是以特定格式解析字符串并将其读取到本机对象的过程。解析过程需要额外的处理来将这种特定格式转换为所需的数据格式。。

相反,您可以只发送对象的实际字节,然后在客户端中读取字节并将其复制到实际的类中。

以下是如何做到这一点-

管道服务器

public class ServerProgram
{
    public static void Main()
    {
        new ServerProgram().Run();
    }
 
    NamedPipeServerStream pipeServer;
    struct RequestCommand
    {
        public int commandId;
        public int commandValue;
    };
    
    IntPtr requestCommandHBuffer;
    byte[] requestCommandBuffer;
    RequestCommand requestCommand;
    int requestCommandBufferSize;
    public void Run()
    {
        // Setup buffers for RequestCommand
        requestCommand = new RequestCommand();
        requestCommandBufferSize = Marshal.SizeOf(requestCommand);
        requestCommandBuffer = new byte[requestCommandBufferSize];
        requestCommandHBuffer = Marshal.AllocHGlobal(requestCommandBufferSize);
        // Create the pipe server
        pipeServer = new NamedPipeServerStream("MY_PIPE_RESOURCE_KEY", PipeDirection.InOut, 1,
            PipeTransmissionMode.Byte,
            PipeOptions.Asynchronous);

        // Wait for the client to connect
        pipeServer.WaitForConnection();
        
        // Set some data in our custom class
        requestCommand.commandId = 123;
        requestCommand.commandValue = 456;

        // Convert the class data to bytes
        Marshal.StructureToPtr(requestCommand, requestCommandHBuffer, true);
        Marshal.Copy(requestCommandHBuffer, requestCommandBuffer, 0, requestCommandBufferSize);
        // Write the data
        pipeServer.Write(requestCommandBuffer, 0, requestCommandBufferSize);

        // Close the pipe server
        pipeServer.Close();
        
    }
}

管道客户端

class ClientProgram
{
    static void Main(string[] args)
    {
        new ClientProgram().Run();
    }

    struct RequestCommand
    {
        public int commandId;
        public int commandValue;
    };
    IntPtr requestCommandHBuffer;
    byte[] requestCommandBuffer;
    int requestCommandBufferSize;
    public void Run()
    {
        // Setup buffers for RequestCommand
        requestCommandBufferSize = Marshal.SizeOf<RequestCommand>();
        requestCommandBuffer = new byte[requestCommandBufferSize];
        requestCommandHBuffer = Marshal.AllocHGlobal(requestCommandBufferSize);

        // Create the pipe client
        var pipeClient = new NamedPipeClientStream(".", "MY_PIPE_RESOURCE_KEY",
            PipeDirection.In, PipeOptions.WriteThrough);
        // Wait until connection (this should continue when the server pipe connected
        pipeClient.Connect();

        // Read the passed RequestCommand bytes
        // NOTE: actualBytesRead should be equal to requestCommandBufferSize. You should check it in production code
        var actualBytesRead = pipeClient.Read(requestCommandBuffer, 0, requestCommandBufferSize);
        // Convert the bytes to instance of RequestCommand
        Marshal.Copy(requestCommandBuffer, 0 /*int startIndex*/, requestCommandHBuffer /*IntPtr destination*/, requestCommandBufferSize);
        var requestCommand = Marshal.PtrToStructure(requestCommandHBuffer, typeof(RequestCommand));
        // This will contain the actual data the sent from the server
        Console.WriteLine("Got message");
    }
}

只有当你运行这两个程序时,它们才会继续执行。在管道客户端代码中放置一个断点,以查看实际数据。