在打开的 NamedPipeClientStream 上重新创建 UTF-8 StreamWriter 会导致服务器上出现额外的 BOM 字符

Recreating an UTF-8 StreamWriter on an open NamedPipeClientStream causes extra BOM characters on the server

提问人:Helena 提问时间:10/2/2023 更新时间:10/2/2023 访问量:41

问:

我有一个保持开放状态,一个保持开放状态。有一个保持打开状态的 UTF-8。有一个 UTF-8,用于打开、写入消息、关闭然后重新打开以写入另一条消息。和 the 都在标志设置为 true 的情况下打开,因此 在关闭时不会关闭。目前为止,一切都好。NamedPipeServerStreamNamedPipeClientStreamNamedPipeServerStreamStreamReaderNamedPipeClientStreamStreamWriterStreamReaderStreamWriterleaveOpenStreamWriterNamedPipeClientStream

我有一个问题,从第二次打开并发送消息开始,.我设法在一个小型示例程序中重新创建了它。它内置于 .NET 6 中。这是示例程序的代码:StreamWriterStreamReader

using System.IO.Pipes;

namespace NamedPipeTest
{
    internal class EncodingTest
    {
        private static NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("mypipe", PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
        private static NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "mypipe", PipeDirection.InOut, PipeOptions.Asynchronous);

        static void Main(string[] args)
        {
            Console.WriteLine("Hello! You are the client. Press any key to send a request.");

            Task[] tasks = new Task[]
            {
                Task.Run(() => Server()),
                Task.Run(() => Client())
            };

            Task.WaitAll(tasks);
        }

        static async Task Server()
        {
            if (!namedPipeServer.IsConnected)
            {
                namedPipeServer.WaitForConnection();
            }

            using (var reader = new StreamReader(namedPipeServer, System.Text.Encoding.UTF8, false, 1024, true))
            {
                while (true)
                {
                    string? line = await reader.ReadLineAsync();
                    Console.WriteLine($"Server: Received request: {line}");
                    if (!string.IsNullOrEmpty(line))
                    {
                        var lineBytes = System.Text.Encoding.UTF8.GetBytes(line);
                        var possibleBOMString = System.Text.Encoding.UTF8.GetString(new byte[] { lineBytes[0], lineBytes[1], lineBytes[2] });
                        if (possibleBOMString.Equals("\uFEFF"))
                        {
                            Console.WriteLine("Server: Whoa, what's up with the extra BOM?");
                        }
                    }
                }
            }
        }

        static async Task Client()
        {
            if (!namedPipeClient.IsConnected)
            {
                namedPipeClient.Connect(6000);
            }

            while (true)
            {
                using (var writer = new StreamWriter(namedPipeClient, System.Text.Encoding.UTF8, 1024, true))
                {
                    Console.ReadKey(true);
                    Console.WriteLine("Client: Received key press, sending request.");
                    string? line = "Hello!";
                    await writer.WriteLineAsync(line);
                    await writer.FlushAsync();
                    Console.WriteLine($"Client: Sent request: {line}");
                }
            }
        }
    }
}

该程序的输出为:

Hello! You are the client. Press any key to send a request.
Client: Received key press, sending request.
Server: Received request: Hello!
Client: Sent request: Hello!
Client: Received key press, sending request.
Server: Received request: ?Hello!
Client: Sent request: Hello!
Server: Whoa, what's up with the extra BOM?
Client: Received key press, sending request.
Server: Received request: ?Hello!
Client: Sent request: Hello!
Server: Whoa, what's up with the extra BOM?

这种情况会无休止地继续下去。因此,该行在发送时不包含额外的 BOM,而是在 读取 之后。这是怎么回事?StreamReader

C# .NET 编码 UTF-8 命名管道

评论

0赞 D A 10/2/2023
而不是作家。WriteLineAsync(行);使用作家。WriteAsync。它来自终点线标记。

答:

2赞 shingo 10/2/2023 #1
  1. System.Text.Encoding.UTF8 提供物料清单

它返回一个 UTF8Encoding 对象,该对象提供 Unicode 字节顺序标记 (BOM)。

  1. StreamWriter.FlushAsync 将 BOM 附加到流中。

  2. StreamWriter.Dispose将调用 ,因此它也会附加一个 BOM。Flush

解决方案是构造一个自定义的 UTF8Encoding 对象:

var enc = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
...
using (var writer = new StreamWriter(namedPipeClient, enc, 1024, true))

评论

0赞 Helena 10/2/2023
这是有道理的。然而,只是跳过冲洗并没有改变我的行为(为什么不呢,我想知道。为什么它不被冲洗仍然有效?另一件事...),我仍然得到BOM。不过,使用自定义 UTF8Enconding 对象解决了这个问题!我会从您的答案中删除带有注释的 Flush 调用的部分,然后我会将其标记为已接受的答案。
1赞 Stephen Cleary 10/2/2023
刷新仅写入尚未写入的 BOM,因此删除刷新不会执行任何操作。正确的解决方案是使用不带 BOM 的 UTF8。
0赞 shingo 10/2/2023
请注意“using”和“while”的顺序,两者都会附加一个 BOM,因此您需要同时修复两者。我假设您问题中的顺序是错别字,因为您在方法中将“使用”放在外面。FlushDisposeServer
0赞 Helena 10/2/2023
不,订单是故意的。如我问题的文本中所述,服务器始终使用相同的 StreamReader,而客户端为每个请求重新创建 StreamWriter。这是基于上下文的,应用程序当然比我给出的示例更复杂,因此顺序。
1赞 shingo 10/2/2023
如果这是有意为之,则必须使用自定义对象。我不认为默认编码有一天会改变,但由于您需要传递接下来的 2 个参数,因此您不能将 null 传递给编码参数。