如何(快速)检查UNC路径是否可用

45

如何检查UNC路径是否可用? 如果共享文件夹不可用,则检查需要大约半分钟:

var fi = new DirectoryInfo(@"\\hostname\samba-sharename\directory");

if (fi.Exists)
//...

有没有更快的方法检查文件夹是否可用?我正在使用Windows XP和C#。


3
判断一个文件夹是否可用的唯一方法是尝试使用它,任何其他检查都可能会给出错误的结果。 - Damien_The_Unbeliever
1
我同意,这是最快的方法。花费时间长并不是由于低效的代码,而是服务器磁盘访问时间所致。 - Johnny DropTables
3
@Johnny: 当分享不可访问(因此服务器无法连接)时,我遇到了延迟问题,而且相当长,长达半分钟。 - thumbmunkeys
2
这是默认的超时机制。 - Daniel Hilgarth
2
@DanielHilgarth - 在进行检查时有没有减少超时时间的方法? - komodosp
@colmde 据我所知,目前还没有这样的设置。如果有的话,我猜想它应该是一个系统范围的设置。 - Daniel Hilgarth
8个回答

26
以下是一个快速而简单的检查方法 - 运行Windows的net use命令并解析输出,找到感兴趣的网络路径(例如\\vault2)和OK所在的行。以下是输出的示例:
C:\>net use
New connections will be remembered.

Status       Local     Remote                    Network

-------------------------------------------------------------------------------
OK           O:        \\smarty\Data       Microsoft Windows Network
Disconnected P:        \\dummy\Data       Microsoft Windows Network
OK                     \\vault2\vault2           Microsoft Windows Network
The command completed successfully.

这不是一个非常 ".netish" 的解决方案,但它非常快,有时这更重要 :-).

以下是执行此操作的代码(LINQPad 告诉我只需要 150ms,所以很好)

void Main()
{
    bool available = QuickBestGuessAboutAccessibilityOfNetworkPath(@"\\vault2\vault2\dir1\dir2");
    Console.WriteLine(available);
}

public static bool QuickBestGuessAboutAccessibilityOfNetworkPath(string path)
{
    if (string.IsNullOrEmpty(path)) return false;
    string pathRoot = Path.GetPathRoot(path);
    if (string.IsNullOrEmpty(pathRoot)) return false;
    ProcessStartInfo pinfo = new ProcessStartInfo("net", "use");
    pinfo.CreateNoWindow = true;
    pinfo.RedirectStandardOutput = true;
    pinfo.UseShellExecute = false;
    string output;
    using (Process p = Process.Start(pinfo)) {
        output = p.StandardOutput.ReadToEnd();
    }
    foreach (string line in output.Split('\n'))
    {
        if (line.Contains(pathRoot) && line.Contains("OK"))
        {
            return true; // shareIsProbablyConnected
        }
    }
    return false;
}

或者你可以采用WMI的方法,正如这个答案所提到的那样,针对如何确保网络驱动器连接到应用程序?


4
建议将 line.Contains("OK") 更改为 line.StartsWith("OK"),以防路径中包含“OK”。 - sparkplug
2
这是一个非常好的解决方案,适用于映射驱动器。您有任何想法如何利用此功能来处理共享但不一定映射的网络路径吗? - PJRobot
请注意:如果此代码将以管理员身份运行,而映射的驱动器是在用户级别上,您将收到“新连接将被记住。列表中没有条目。”的提示信息。 - itsho
这需要使用net use命令映射每个网络位置,但对于未映射的直接路径\10.10.10.10\foo\bar,这将无法工作 :( - shjeff

14

在我的项目中,我使用了 System.IO:

if (Directory.Exists(@"\\hostname\samba-sharename\directory")) { ...

现在运行得相当快...


2
是的,这是为了测试存在性和可用性...也许在不可用时网络管理员需要设置某种超时,这就是为什么你需要等待30秒的原因吗..? - Fabien

5
在我的一个项目中,我需要检查服务器连接是否已建立。我使用了一个TCP Socket来异步检查服务器是否可达。不知道你是否可以使用它来检查网络共享。异步TCP Socket连接非常快,我在60毫秒内测试了10次连接。也许你可以尝试一下?
编辑:这是我在项目中使用的异步Socket。使用这个类来检查特定的IP地址或地址。希望对你有用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace Base.BaseObjects
{
    public class AsynchronousClient
    {
        #region Properties

        private int _port = 0000, currentTry = 0, _buffersize, _fastpingdelay = 80;
        private string _server = "localhost";
        private Socket client;
        private static IPEndPoint remoteEP;

        // Delegates & Events
        public delegate void SendMessageDelegate(string message);
        public event SendMessageDelegate SendMessageEvent;
        public delegate void ConnectionStatusDelegate(bool connected, bool reconnect);
        public event ConnectionStatusDelegate ConnectionStatusChanged;

        // ManualResetEvent instances signal completion.
        private static ManualResetEvent connectDone = new ManualResetEvent(false);
        private static ManualResetEvent sendDone = new ManualResetEvent(false);
        private static ManualResetEvent receiveDone = new ManualResetEvent(false);

        /// <summary>
        /// Port to monitor
        /// </summary>
        public int Port { get { return _port; } }

        /// <summary>
        /// Number of packages to buffer until system reports connection loss
        /// </summary>
        public int BufferSize { get { return _buffersize; }  }

        /// <summary>
        /// Time in milliseconds between two pings
        /// </summary>
        public int FastPingDelay { get { return _fastpingdelay; } }

        /// <summary>
        /// Servername to connect to
        /// </summary>
        public string Server
        {
            get { return _server; }
            set
            {
                _server = value;
                // Resolve the remote endpoint for the socket.
                try
                {
                    IPAddress ipAddress = (IPAddress)Dns.GetHostAddresses(value)[0];
                    remoteEP = new IPEndPoint(ipAddress, Port);
                }
                catch (SocketException ex)
                {
                    SendMessage(ex.Message);
                }
            }
        }

        #endregion

        #region Events & Delegates

        protected void SendMessage(string message)
        {
            if (SendMessageEvent != null)
                SendMessageEvent(message);
        }

        protected void UpdateConnectionStatus(bool connected, bool reconnect)
        {
            if (ConnectionStatusChanged != null)
                ConnectionStatusChanged(connected, reconnect);
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket client = (Socket)ar.AsyncState;

                // Complete the connection.
                client.EndConnect(ar);

                SendMessage(String.Format("Socket connected to {0}", client.RemoteEndPoint.ToString()));
                //UpdateConnectionStatus(true, false);

                // Signal that the connection has been made.
                connectDone.Set();
            }
            catch (Exception e)
            {
                SendMessage(e.ToString());
                UpdateConnectionStatus(false, true);
            }
        }

        #endregion

        #region methods

        public AsynchronousClient(int port, string server)
        {
            _port = port;
            Server = server;
            _buffersize = 10;
            _fastpingdelay = 20;
        }

        public void CreateSocket()
        {
            try
            {
                StopClient();
            }
            catch (Exception ex)
            {
                SendMessage(ex.Message);
            }
            finally
            {
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
        }

        public bool FastPingSocket()
        {
            for (currentTry = 0; currentTry <= BufferSize; currentTry++)
            {
                try
                {
                    CreateSocket();
                    client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
                    connectDone.WaitOne();
                    System.Threading.Thread.Sleep(FastPingDelay);
                    client.Shutdown(SocketShutdown.Receive);
                    connectDone.WaitOne();
                    client.Close();
                    return true;
                }
                catch (SocketException ex)
                {
                    SendMessage(ex.Message);
                }
                catch (ObjectDisposedException ex)
                {
                    currentTry--;
                    SendMessage(ex.Message);
                    CreateSocket();
                }
                catch (NullReferenceException ex)
                {
                    currentTry--;
                    SendMessage(ex.Message);
                    CreateSocket();
                }
                catch (ArgumentNullException ex)
                {
                    SendMessage(ex.Message);
                    CreateSocket();
                }
                catch (InvalidOperationException ex)
                {
                    SendMessage(ex.Message);
                    CreateSocket();
                    currentTry--;
                }
                finally
                {
                    StopClient();
                }
            }
            UpdateConnectionStatus(false, true);
            return false;
        }

        public void StopClient()
        {
            // Release the socket.
            try
            {
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }
            catch (Exception) { }
            finally
            {
                UpdateConnectionStatus(false, false);
            }
        }

        #endregion

    }
}

编辑: 请不要仅仅复制/粘贴代码。尝试理解代码,以便您可以将其用于自己的利益,并根据自己的需求进行微调。

谢谢您的建议,我会检查主机是否可达,如果是,则尝试访问共享。 - thumbmunkeys

4

最后我“作弊”了,只是对主机进行了ping操作,这个做法其实是合理的,因为我需要检查的就是主机是否通畅。

private bool HostExists(string PCName)
{
    Ping pinger = new Ping();

    try
    {
        PingReply reply = pinger.Send(PCName);
        return reply.Status == IPStatus.Success;
    }
    catch
    {
        return false;
    }
    finally
    {
        pinger.Dispose();
    }

}

这种方法最适合我,因为它速度快、简单可靠。

注意:这里使用了“using System.Net.NetworkInformation;” - Maxter
2
注意:如果主机之间或主机上的防火墙禁用了ICMP,则此方法将无法工作。 - Lachlan Ennis

3

我尝试了上面建议的ping方法,但由于我使用的是OpenDNS,它对我不起作用。以下是一个对我有效的函数:

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/// <summary>
/// A quick method to test is the path exists 
/// </summary>
/// <param name="s"></param>
/// <param name="timeOutMs"></param>
/// <returns></returns>
public static bool CheckPathExists(string s, int timeOutMs = 120) {
    if (s.StartsWith(@"\\")) {
        Uri uri = new Uri(s);
        if (uri.Segments.Length == 0 || string.IsNullOrWhiteSpace(uri.Host))
            return false;
        if (uri.Host != Dns.GetHostName()) {
            WebRequest request;
            WebResponse response;
            request = WebRequest.Create(uri);
            request.Method = "HEAD";
            request.Timeout = timeOutMs;
            try {
                response = request.GetResponse();
            } catch (Exception ex) {
                return false;
            }
            return response.ContentLength > 0;

            // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
            // Do a Ping to see if the server is there
            // This method doesn't work well using OPenDNS since it always succeeds
            // regardless if the IP is a valid or not
            // OpenDns always maps every host to an IP. If the host is not valid the 
            // OpenDNS will map it to 67.215.65.132
            /* Example:
                C:\>ping xxx

                Pinging xxx.RT-AC66R [67.215.65.132] with 32 bytes of data:
                Reply from 67.215.65.132: bytes=32 time=24ms TTL=55
                */

            //Ping pingSender = new Ping();
            //PingOptions options = new PingOptions();
            // Use the default Ttl value which is 128,
            // but change the fragmentation behavior.
            //options.DontFragment = true;

            // Create a buffer of 32 bytes of data to be transmitted.
            //string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
            //byte[] buffer = Encoding.ASCII.GetBytes(data);
            //int timeout = 120;
            //PingReply reply = pingSender.Send(uri.Host, timeout, buffer, options);
            //if (reply == null || reply.Status != IPStatus.Success)
            //    return false;
        }
    }
    return File.Exists(s);
}

2

这可能是最快的方法,延迟取决于网络速度/磁盘访问等。

如果这会给用户带来延迟,您可以尝试异步检查。


我可以将其变成异步的,但用户仍需等待半分钟才能获取文件。 - thumbmunkeys
1
如果您在资源管理器中导航到相同的 UNC 路径,是否仍然会看到相同的延迟? - Mark Redman
1
我认为在这种情况下延迟是不可避免的,关键在于如何优雅地向用户展示。 - Mark Redman
1
@pivot:是的,这需要相当长的时间。这不仅适用于Windows XP;所有版本的Windows都有类似的超时时间。可能有一种方法可以改变它,但这对您的用户没有任何帮助。马克的建议是用漂亮的GUI来掩盖它,这大概是你唯一的选择。甚至连资源管理器都不会费心去做那个。如果你足够分散用户的注意力(比如说用进度条),他们可能就不会在意了。特别是如果你的应用程序不必反复执行这个操作(而且它不应该这样做)。 - Cody Gray
2
@pivot:根据您最初的问题,不,您不能(也不应该)提前检查驱动器是否可用。这会导致不可避免的竞态条件,并且没有多大意义。正如其他人建议的那样,最好尝试使用驱动器并检测失败。无论如何,您都必须编写该代码。 - Cody Gray
显示剩余3条评论

0
也许你应该尝试创建文件夹,如果它已经存在,它会返回一个错误,你可以捕获这个错误。应该没有默认的超时时间。

3
我尝试过那个方法,如果共享不可用,创建文件夹也会有同样长的超时时间(如果情况不是这样,那就令人惊讶了)。 - thumbmunkeys

0
接受的答案在我的情况下不起作用,因为即使驱动器可用(当我的VPN重新连接时),它仍然保持“断开连接”状态,直到我访问该驱动器。
相反,以下方法有效:对IP进行一次快速ping测试。 例如: ping 192.168.10.98 -n 1 -w 100

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接