首页 > 其他分享 >dotnet 6 使用 File.Exists 判断管道是否存在将让下次连接失败

dotnet 6 使用 File.Exists 判断管道是否存在将让下次连接失败

时间:2022-09-20 20:02:14浏览次数:88  
标签:git Exists var 管道 判断 File dotnet

我尝试在 dotnet 6 使用 File.Exists 判断管道是否存在,如果管道存在再进行连接。然而这个逻辑将会接下来的 NamedPipeClientStream 调用 Connect 连接失败

这个问题似乎是 CLR 底层的问题,我将问题报告给官方,请看 Using File.Exists to check the pipe created will make the NamedPipeClientStream connect fail · Issue #69604 · dotnet/runtime

使用 File.Exists 判断管道是否存在的代码如下

File.Exists(@"\\.\pipe\" + PipeName);

然而以上代码将会在 dotnet 6 下让 NamedPipeClientStream 调用 Connect 连接失败。这个逻辑如果放在 .NET Framework 下运行,是能成功的

最简复现步骤如下,先使用 NamedPipeServerStream 启动管道服务

void StartServer()
{
    var server = new NamedPipeServerStream(PipeName,
        PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1024, 1024);

    server.BeginWaitForConnection(OnWaitForConnection, server);
}

接下来使用 File.Exists 判断管道是否存在

File.Exists(@"\\.\pipe\" + PipeName);

再使用 NamedPipeClientStream 进行连接

void StartClient()
{
    var localServer = ".";
    var pipeDirection = PipeDirection.InOut;
    var client = new NamedPipeClientStream(localServer,
        PipeName, pipeDirection, PipeOptions.Asynchronous);

    var timeout = 1000 * 5;
    client.Connect(timeout);
}

运行代码,可以看到 Connect 方法抛出 TimeoutException 错误

核心原因是在 .NET 6 通过 GetFileAttributesW 去判断管道是否存在,然而根据堆栈网 的描述,通过 GetFileAttributesW 去判断一个非文件系统的对象,会有非预期的行为。这也就是管道连接失败的原因。那为什么 .NET Framework 没问题?因为 .NET Framework 是先调用 FindFirstFile 进行判断

因此一个解决方法是采用和 .NET Framework 一样的 FindFirstFile 方法进行判断管道是否存在,代码如下

        private static bool IsPipeExists(string pipeName)
        {
            try
            {
                // 不要用 File.Exists 判断,内部会调用 GetFileAttributes 导致管道无法被连接

                unsafe
                {
                    // 这里是一个结构体,但是不关心内容,直接栈上分配点空间给它
                    var findFileData = stackalloc byte[604];

                    var file = FindFirstFile(@"\\.\pipe\" + pipeName, (IntPtr)findFileData);

                    const nint INVALID_HANDLE_VALUE = -1;

                    if (file != INVALID_HANDLE_VALUE)
                    {
                        FindClose(file);
                        return true;
                    }
                }
            }
            catch
            {
                
            }
            return false;
        }


        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "FindFirstFileW", ExactSpelling = true)]
        private static extern SafeFileHandle FindFirstFile([In] string lpFileName, [In] IntPtr lpFindFileData);

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "FindClose", ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool FindClose([In] IntPtr hFindFile);

如果不想碰不安全代码,也可以采用判断文件夹里面的文件是否存在的方法判断管道是否存在

Directory.EnumerateFiles(@"\\.\pipe\", PipeName).Any();

只是文件夹的判断方法会比使用 FindFirstFile 的速度慢一点点,我测试大概是 3-5 毫秒左右

更多请看 Detecting that NamedPipe exists - Andrii Snihyr

本文以上的测试代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin e26bdd0bced93db8edf8be18f1062e571f3d1861

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 HallwhallkernarbafejaNakeldibi 文件夹

标签:git,Exists,var,管道,判断,File,dotnet
From: https://www.cnblogs.com/lindexi/p/16712283.html

相关文章

  • dotnet 6 精细控制 HttpClient 网络请求超时
    本文告诉大家如何在dotnet6下使用HttpClient更加精细的控制网络请求的超时,实现HttpWebRequest的ReadWriteTimeout功能本文将介绍如何在HttpClient控制以下网络......
  • dotnet 6 通过 DOTNET_ROOT 让调起的应用的进程拿到共享的运行时文件夹
    我的应用是独立发布的,在用户的设备上不需要额外去安装.NET运行时。但是我的应用有一个需求是下载另一个应用作为插件,由本应用调起插件进程。本文告诉大家如何解决调用插......
  • Java基础之ClassFile文件结构
    本文相关知识均来自:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html.class文件都遵循ClassFile结构:ClassFile{u4magic;u2......
  • [转]启动mysql服务时一直提示ERROR The server quit without updating PID file
    原文地址:启动mysql服务时一直提示ERRORTheserverquitwithoutupdatingPIDfile_zqin0的博客-CSDN博客centos7中使用二进制文件部署的mysql启动mysql服务时一直提......
  • DockerFile
    Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。具体解释:镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以......
  • java FileInputStream的相对路径
    javaFileInputStream的相对路径是相对于src目录的publicvoidbufferTest(){try(FileChannelchannel=newFileInputStream("src/main/resources/data.txt").get......
  • node.js 使用教程-3.gulp-file-include 详细教程
    前言gulp-file-include是gulp插件,它提供了一个include方法让我们可以像后端模板那样把公共部分的页面导入进来,实现html复用。环境准备gulp-file-include是gulp的......
  • FileInputStream,OutputStreamWriter
    packagecom.gao.IO;importjavax.xml.stream.FactoryConfigurationError;importjava.io.*;publicclassTest06{publicstaticvoidmain(String[]args)thr......
  • FileReader,FileWriter
    FileReader一个一个字符的读:packagecom.gao.IO;importjava.io.File;importjava.io.FileNotFoundException;importjava.io.FileReader;importjava.io.IOExcepti......
  • maven profile
    <profiles><profile><id>dev</id><activation><activeByDefault>true</activeByDefault></activ......