You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# Named Pipelines编码异常及连接关闭问题排查求助

问题描述

首次用Named Pipelines开发兼具客户端、服务端功能的独立exe,服务端拥有高权限。目前碰到两个问题:

  • 服务端返回客户端的消息,除首次响应外都以'?'开头,虽使用同一writeClient函数且编码一致仍未解决;
  • 需要确认连接关闭的实现是否规范。

客户端输出

已连接。
已安装版本: 1.2.8
?最新版本: 1.2.85
?需要更新
?不带'-Check'参数重新运行以执行静默安装。
?关闭
连接已关闭。

主程序代码

using System;
using System.Diagnostics;
using System.Xml.Linq;
using System.Runtime.Versioning;
using System.IO.Pipes;
using System.Text;
using System.Net;
using System.ServiceProcess;
using System.Security.Authentication.ExtendedProtection;

[SupportedOSPlatform("windows")]

class DAQUpdate
{
    static void Main(string[] args)
    {       
        if (args.Length > 0)
        {
                //检查是否有同程序以服务运行,有的话用服务端,因为它有高权限
                try
                {
                    using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "DAQUpdater", PipeDirection.InOut))
                    {
                        //1秒超时连接服务端,超时会返回错误
                        pipeClient.Connect(1);
                        //再次确认连接状态
                        if(pipeClient.IsConnected)
                        {
                            Console.WriteLine("Connected.");
                            // 发送参数给服务端
                            using (StreamWriter writer = new StreamWriter(pipeClient,Encoding.UTF8,-1,true))
                            {
                                //用;分隔参数,因为没法直接传对象
                                writer.WriteLine(string.Join(';', args));
                                writer.Flush();
                            }
                            //读取服务端所有响应,最后一条是Close
                            string response = "";                               
                            using (StreamReader reader = new StreamReader(pipeClient, Encoding.UTF8))
                            {
                                while(!response.Contains("Close"))
                                {                                     
                                    response = reader.ReadLine();
                                    Console.WriteLine(response);
                                }
                                pipeClient.Close();
                            }
                            Console.WriteLine("Connection closed.");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine("No Connection.");
                }            
        }
        else
        {
            NamedPipeServer.runServer();
        }
    }
    public static void update(string[] args)
    {
        NamedPipeServer.writeClient("hello");
        NamedPipeServer.writeClient(args[0]);
        NamedPipeServer.writeClient("Testing");
        NamedPipeServer.writeClient("Close");
    }
}

服务端函数代码

// NamedPipeServer.cs
using System.IO.Pipes;
using System.Text;

public class NamedPipeServer
{
    private static NamedPipeServerStream pipeServer;
    public static void runServer()
    {
        try
        {            
            while(true)
            {
                //改成全局变量,方便其他函数调用writeClient给客户端发消息
                pipeServer = new NamedPipeServerStream("DAQUpdater", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
                {
                    Console.WriteLine("Waiting for connection...");
                    pipeServer.WaitForConnection();
                    Console.WriteLine("Client connected.");
                    // 读取并处理客户端发来的命令
                    using (StreamReader reader = new StreamReader(pipeServer, Encoding.UTF8,true,-1,true ))
                    {
                        string command = reader.ReadLine();
                        string[] commandargs = command.Split(';');
                        Console.WriteLine($"Received command: {commandargs[0]}");
                        //运行更新检查程序,同时会给客户端发消息
                        DAQUpdate.update(commandargs);
                        // 给客户端返回响应(如果需要)
                        using (StreamWriter writer = new StreamWriter(pipeServer, Encoding.UTF8, -1 , true))
                        {
                            writer.WriteLine("Close");
                            writer.Flush();
                        }
                    }
                    pipeServer.Close();
                    Console.WriteLine("Connection closed.");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
    public static void writeClient(string messageTxt)
    {
        //允许其他函数给客户端发送更新消息
        if(pipeServer != null)
        {
            if(pipeServer.IsConnected)
            {
                using (StreamWriter writer = new StreamWriter(pipeServer, Encoding.UTF8, -1 , true))
                {
                    Console.WriteLine(writer.Encoding);
                    writer.WriteLine(messageTxt);
                    writer.Flush();
                }
            }
        }                
    }
}
问题解决方案

1. 消息开头出现'?'的问题

问题根源是重复创建流包装器导致的编码不同步:
服务端中,你先创建StreamReader包裹管道流,之后又在writeClient里多次创建StreamWriter包裹同一个管道流。每个流包装器都有自己的内部缓冲区,多实例操作同一底层流会导致字节读写错位,出现乱码(表现为开头的'?')。

修复方案:
连接建立后一次性创建StreamReader和StreamWriter,复用这两个实例直到连接关闭,避免重复包装。

修改后的核心代码示例:

// 服务端runServer方法修改
public static void runServer()
{
    try
    {            
        while(true)
        {
            using (NamedPipeServerStream pipeServer = new NamedPipeServerStream("DAQUpdater", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous))
            {
                Console.WriteLine("Waiting for connection...");
                pipeServer.WaitForConnection();
                Console.WriteLine("Client connected.");
                
                // 一次性创建读写流并复用
                using (StreamReader reader = new StreamReader(pipeServer, Encoding.UTF8, true, -1, true))
                using (StreamWriter writer = new StreamWriter(pipeServer, Encoding.UTF8, -1, true))
                {
                    string command = reader.ReadLine();
                    string[] commandargs = command.Split(';');
                    Console.WriteLine($"Received command: {commandargs[0]}");
                    
                    // 传入复用的writer给update方法
                    DAQUpdate.update(commandargs, writer);
                    
                    writer.WriteLine("Close");
                    writer.Flush();
                }
                
                Console.WriteLine("Connection closed.");
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error: {ex.Message}");
    }
}

// 修改writeClient方法,直接传入已创建的writer
public static void writeClient(StreamWriter writer, string messageTxt)
{
    if(writer != null && !writer.BaseStream.IsClosed)
    {
        Console.WriteLine(writer.Encoding);
        writer.WriteLine(messageTxt);
        writer.Flush();
    }                
}

// 主程序update方法修改,接收writer参数
public static void update(string[] args, StreamWriter writer)
{
    NamedPipeServer.writeClient(writer, "hello");
    NamedPipeServer.writeClient(writer, args[0]);
    NamedPipeServer.writeClient(writer, "Testing");
    NamedPipeServer.writeClient(writer, "Close");
}

2. 连接关闭的规范实现

当前代码的关闭逻辑存在两个问题:

  • 客户端在using块内手动调用pipeClient.Close(),但using语句本身会自动释放资源,重复关闭可能引发异常;
  • 服务端中pipeServer的using块使用不完整,存在多余的大括号,没有用using正确包裹管道流实例。

规范实现方式:

  • 优先使用using语句自动管理管道流和读写流的资源,using块结束时会自动调用Dispose(),该方法会自动关闭流;
  • 除非需要提前关闭且明确后续不再使用,否则不要手动调用Close()。

修改后的客户端读取部分:

using (StreamReader reader = new StreamReader(pipeClient, Encoding.UTF8))
{
    string response;
    while((response = reader.ReadLine()) != null)
    {                                     
        Console.WriteLine(response);
        if(response.Contains("Close"))
            break;
    }
}
// 移除pipeClient.Close(),外层using(pipeClient)会自动处理关闭

修改后的服务端runServer方法已在上面的修复示例中体现,核心是用using完整包裹pipeServer实例,确保资源自动释放。

内容的提问来源于stack exchange,提问作者Furbs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:35:54