Click here to Skip to main content
Click here to Skip to main content

Locate SQL Server instances on the local network

By , 24 Nov 2005
 

Introduction

This articles shows how to retrieve a list of PCs on the local network which are running MS SQL Server, and gets information about the instances, such as server name, instance name, version, and databases.

Background

To get a list of the nearby SQL Servers is as simple as typing "osql /L" on a command prompt. But getting them programmatically, for use inside an application, is a bit trickier. The most common use for this would be to create a connection string building form in managed code, which will be the subject of my next article.

Note also, that while the subject matter is SQL Server, the first half of this article deals mostly with sockets.

To create a list of SQL Server instances, I first tried several different methods, all of which had two things in common: they took a very long time, and they were only marginally successful at finding SQL Servers instances on the network. Eventually, I had the idea of running "osql -L" with a packet sniffer running (I used Ethereal), to see how osql was doing it.

Its method was to send out a Broadcast UDP packet on port 1434, with the contents of just a byte of 0x02, to which each server would respond. Sending such a message is quite simple:

Socket socket = new Socket(AddressFamily.InterNetwork, 
              SocketType.Dgram, ProtocolType.Udp );
//  For .Net v 1.1 the options are cumbersome & hidden.
    socket.SetSocketOption(SocketOptionLevel.Socket, 
              SocketOptionName.Broadcast, 1);
    socket.SetSocketOption(SocketOptionLevel.Socket, 
              SocketOptionName.ReceiveTimeout, 3000);
    
//  For .Net v 2.0 it's a bit simpler
//  socket.EnableBroadcast = true;    // for .Net v2.0
//  socket.ReceiveTimeout = 3000;     // for .Net v2.0

IPEndPoint ep = new IPEndPoint(IPAddress.Broadcast, 1434);
byte[] msg = new byte[] { 0x02 };
socket.SendTo(msg, ep);

It's vitally important that you set the timeout. Without it, the Receive method will just keep waiting (forever!) until it gets something. However, since we'll be waiting for responses from an unknown number of servers, we have to decide to give up at some point. I've set that point, initially, at 3 seconds. Before I realized that the Broadcast & Timeout options were available (but hidden) on the Socket class, I had this elaborate class which derived from UdpClient and implemented asynchronous receiving.

As it turns out, with the proper options set, getting a response is as simple as you'd hope.

byte[] bytBuffer = new byte[256];
socket.Receive(bytBuffer);

The Receive method will get the response from one server, so we'll have to put that code in a loop to get all of them. Of course, once we've gotten a response, we need to interpret it. For this, I've created a simple class called SqlServerInfo, which I'll explain further in a minute.

After we've received, processed and stored the response, we just keep looping until we don't get a response within the timeout. The downside of using a timeout, is that when it is reached, the Socket throws a SocketException, which means we have to incur the overhead time to process a throw every time we call this method. This is in addition to the timeout delay itself. I was able to find a way to minimize this.

I initially set the timeout to 3 seconds. The problem here was that after each response, I'd call Receive again, and the timeout timer would start over. So the total time for the method would be: the total actual processing time + the timeout + the throw overhead. This was about 5 seconds in my tests. This seems way too long to me -- remember, this is something we are going to want to do in the Page_Load of a form. One thing I noticed was that after I send the broadcast message, there would be a short delay as the message went out, the remote servers received it and prepared their responses. However, after that delay, all the responses would come in a pack. So, I decided, after I processed the first response, to reset the timeout to 0.3 seconds. This cut the total time down to about 1-2 seconds, a much more reasonable delay. (However, if there were no servers at all on the network, it would still take 3+ seconds to realize that.)

Now, back to interpreting the response we've gotten, which is quite simple. The first three bytes are a byte of 0x05, followed by two bytes giving the length of the rest which is straight ASCII text of pairs of items, separated by semicolons:

data item 1 name ; data item 1 value ; data item 2 name  ; data item 2 value;

or more specifically:

ServerName;DATA001;InstanceName;MSSQLSERVER;IsClustered;No;Version;8.00.194;tcp;1433

To make use of this, we'll have to convert from a byte array of ASCII characters to a .NET string of UNICODE characters, which the framework is nice enough to provide a method for (although it does bury in under System.Text.ASCIIEncoding.ASCII). Then, separate the parts, and move the the data values to properties where they can be accessed easier.

Of course, while the information provided in the broadcast response is useful, it doesn't provide one vitally important piece of data: the list of databases on that server. To get that, we need to use more traditional database access methods.

But, to do that, we are going to have to connect to the server, which means a connection string, which means a username & password. SqlServerInfo provides read/write properties for those as well as a boolean IntegratedSecurity option (the other properties are read-only).

Once we've established a connection, getting the list of catalogs is a method built right into the OleDbConnection object:

DataTable schemaTable = 
   myConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Catalogs, null);

Using the code

Everything is packaged in the class SqlServerInfo. It has one static method, Seek(), which gets the information about the SQL Server instances on the network, and returns an array of SqlServerInfo objects.

SqlServerInfo[] servers = SqlServerInfo.Seek();

Each SqlServerInfo object contains a number of properties describing that particular MS SQL Server instance. Each of these is a read-only property.

  • public string ServerName
  • public string InstanceName
  • public bool IsClustered
  • public string Version
  • public int TcpPort
  • public string NamedPipe
  • public IPAddress Address

InstanceName is typically "MSSQLSERVER", which is the default if it isn't given a specific name at installation. Version should be "8.0.xxx" for SQL Server 2000. And TcpPort will typically be 1433.

Note that, although Address is a property, in the current implementation it will always be null --- until I can figure a way of finding out what the server's IP address is. The Socket.Receive method doesn't say what machine is sending the data -- it is just assumed to be the machine you just transmitted to -- the naiveté of which is apparent when one sends a broadcast message. (If anyone knows how to get that information out of a Socket, please let me know.)

Next there are four Read/Write properties, which must be set to get any more information out of this class:

  • public string UserId
  • public string Password
  • public bool IntegratedSecurity
  • public int TimeOut

These establish how we are going to attempt to connect to that server. IntegratedSecurity defaults to true, and TimeOut defaults to 2 seconds, so if these work for you, you don't have to do anything more. Setting either UserId or Password sets IntegratedSecurity to false.

The final property retrieves a list of databases available on the server.

  • public StringCollection Catalogs

The first time it is called, it will attempt to connect to the server, so the above property needs to be set right.

Finally, there are two methods:

  • public bool TestConnection()
  • public override string ToString()

TestConnection is just as its name says --- it tests if you can connect with the given userID/password.

ToString returns either the ServerName (if the InstanceName is the default) or ServerName / InstanceName. Either way it is what you need to specify as the Data Source in a connection string. As a ToString, you can set the DataSource property of a ListBox or similar control to an array of SqlServerInfo objects, and the appropriate values will be displayed.

SqlServerInfo Data = servers[0];
Console.WriteLine(Data.ToString());
Console.WriteLine("Version:", Data.Version);

Data.IntegratedSecurity = true;
foreach(string Catalog in Data.Catalogs)
{
    Console.WriteLine("    {0}", Catalog);
}

History

  • 20-Nov 2005 - v 1.0 Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

James Curran
Web Developer Tallan, LLC
United States United States
Member
20+ years as a developer : Assembly, C, C++ and C# (in that order) with sidelines in ASP/VBScript, ASP.Net, JavaScript, Perl, QuickBasic, VisualBasic, plus a few others which I'm not going to mention because if I did someone might ask me to use them again (shudder)
 
Microsoft MVP in VC++ (1994-2004)
 
I also run www.NJTheater.com as a hobby.
 
Full resume & stuff at NovelTheory.com
 
Underused blog at HonestIllusion.com

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberAkshay Srinivasan21 Feb '13 - 0:28 
Thanks very useful was not able to find any good way to discover SQL Servers on the local subnet.
GeneralMy vote of 5memberFrancis Judge9 Dec '11 - 1:39 
Great, simple code, well written and still useful years later
GeneralGreat!membersurecan18 Jul '11 - 10:41 
Thank you very much! Thumbs Up | :thumbsup:
GeneralHow to use?membertoeknee1010 May '11 - 6:07 
I am a newbie and was wondering.. How do I run this script? What do I use?
 
Thanks for any help offered..
GeneralDont understand ping to instancesmemberjuliotrujilloleon12 Jan '10 - 7:30 
Sorry, I'am spanish and dont speak englis very well, and your text in the post dont translate well
 
I don't understand how to search intances across local network, I supose that your app send a generic ping to port 1433 to all machines on network by using brodcast that reply the signal of ping to all computers, ad them response when have a SQL listening to port 1433
 
¿This explanation it's correct? I understand well the app?
 
Thaks lot
QuestionSome nicely written code... but why all the extra work?memberDrew Stegon18 Dec '09 - 8:51 
While trying to figure this problem out for our company, I ran into your article. I converted it into vb.net and used some newer types such as List(Of ) instead of arrays. But, then in running your code, it didnt find all the instances we had on our development servers. Thought it might be the vb code vs the c# code, but your raw code didnt work either. We run both SQL 2000 and SQL 2005, and your code would not find the 2005 stuff. So I started to research MS for sql stuff and found the DataSourceEnumerator (which I sadly have to say if I would have just read other comments, it would have saved me HOURS!!... live and learn).
 
Much easier to allow Windows to retrieve. Although MS has a disclaimer that states this method may "not" retrieve all the servers(data sources) and catalogs, we have never run into a time where it wasn't accurate.
 
' Retrieve the enumerator instance and then the data.
        Dim instance As SqlDataSourceEnumerator = _
         SqlDataSourceEnumerator.Instance
        table = instance.GetDataSources()
 
Once it is in the datatable you can use the datasources stored in the table and use your GetCatalogs method (very nice) and retrieve the catalogs easily and store those in a datatable to present in a treeview datagridview or even list or combo box controls.
 
Private Function GetCatalogs() As StringCollection
        Dim restrictions As Object()
        Dim Catalogs As New StringCollection
        restrictions = Nothing
 
        Try
            Dim myConnection = GetConnection()
            myConnection.Open()
            Dim schemaTable As DataTable = myConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Catalogs, restrictions)
            myConnection.Close()
 
            For Each dr As DataRow In schemaTable.Rows
                Dim CatName As String = dr(0).ToString
                Catalogs.Add(CatName)
            Next
 
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
 
        Return Catalogs
 
    End Function
 
    Private Function GetConnection() As OleDbConnection
        Dim myConnString As String
 
        'Using a server that doesnt require ID and password, so just commented out
        'If IntegratedSecurity Then
        myConnString = String.Format("Provider=SQLOLEDB;Data Source={0};Integrated Security=SSPI;Connect Timeout={1}", DataSource, Timeout)
        'Else
        'myConnString = String.Format("Provider=SQLOLEDB;Data Source={0};User Id={1};Password={2};Connect Timeout={3}", DataSource, UserId, Password, Timeout)
        'End If

        Return New OleDbConnection(myConnString)
 
    End Function

GeneralGreatmemberdexter_wrocek16 Oct '09 - 1:51 
Thanks. Great job!
GeneralSqlDataSourceEnumeratormemberMotz31 Jul '09 - 8:34 
Instead of using SMO since there are DLL problems if Sql server isn't installed on the machine you can use SqlDataSourceEnumerator
 
DataTable dt = System.Data.Sql.SqlDataSourceEnumerator.Instance.GetDataSources();
 
foreach (System.Data.DataRow row in dt.Rows)
{
string serverName = row["servername"] as string;
string serverInstance = row["instancename"] as string;
}
 

there are more things you can get like the version number as well.
 
Enjoy.
QuestionDoes not show localhostmemberDEGT9 Jul '09 - 21:05 
I have an instance of SQL Server 2005 running on my own machine (where I ran this code snippet) and it shows all other SQL servers on our network except the one on localhost. Any ideas?
 
http://www.PanamaSights.com/
http://www.coralys.com/
http://www.virtual-aviation.info/

AnswerRe: Does not show localhostmemberMember 26432587 Oct '09 - 3:37 
Check if the following services are enabled and running locally:
 
Computer Browser
Server
Sql Browser
 
Greetz....
GeneralSMO Short commingmembercsniffer31 May '09 - 2:33 
Thx. This was just what I needed. I tried using SMO but it wouldn't see another machine on my network as the guest account is turned off. While trying to solve this I found your post which seemed better than SMO to me for discovering SQL servers.
 
Thx Again!
 
Penguin power

GeneralSqlDataSourceEnumerator.Instance.GetDataSources()memberJeremy Long9 Mar '09 - 13:36 
Is there something wrong with SqlDataSourceEnumerator.Instance.GetDataSources()? why not just use that?
GeneralIP Address fixmemberSalvador S. Delvisco Jr.19 Sep '08 - 10:34 
First of all, great work. This code helped me a lot.
 
I used your code, and then modified it as "Member 1053820" had posted to make sure I had all of the instances of SQL Server in my list. Then I worked on getting the IP Address to correctly function instead of being null.
 
I came up with the following fix to the Address Public Property.
 
/// <summary>
        /// Gets the IP address.
        /// </summary>
        /// <value>The address</value>
        /// <remarks>Corrected to return actual IP Address</remarks>
        public IPAddress Address
        {
            get
            {
                IPHostEntry ipEntry = Dns.GetHostEntry(oServerName);
                oIP = ipEntry.AddressList[0];
                return oIP;
            }
        }

GeneralGreat work James I made slight modifications for my purposes that i think might be useful for some.memberMember 105382019 Aug '08 - 5:35 
First I added new fields: Collation and Authentication Mode, couse, well, I needed them.
 
private string oCollation;
private string oAuthMode;
 
then I made public properties for them:
 
  /// <summary>
        ///  Gets the server collation
        /// </summary>
        /// <value>Collation</value>
        public string Collation
        {
            get
            {
                return oCollation;
            }
        }
 
        /// <summary>
        /// Gets the server Authentication mode
        /// </summary>
        /// <value>Auth mode</value>
        public string AuthMode
        {
            get
            {
                return oAuthMode;
            }
        }
 
Then I modified TestConnection() method to use native connection provider and to get collation and authentication mode while testing the connection:
 
public bool TestConnection()
        {
 
            SqlConnection conn = this.GetConnection();
            //OleDbConnection conn = this.GetConnection();
            bool success = false;
            try
            {
                conn.Open();
 
                SqlCommand command = new SqlCommand("SELECT CONVERT (varchar, SERVERPROPERTY('collation'))");
                //OleDbCommand command = new OleDbCommand("SELECT CONVERT (varchar, SERVERPROPERTY('collation'))");
                command.Connection = conn;
                oCollation = (string)command.ExecuteScalar();
                command.CommandText = "SELECT CONVERT (int, SERVERPROPERTY('IsIntegratedSecurityOnly'))";
                switch ((int)command.ExecuteScalar())
                {
                    case 1: oAuthMode = "Windows only"; break;
                    case 0: oAuthMode = "Mixed mode"; break;
                    default: oAuthMode = "Greska"; break;
                }
 

                conn.Close();
                success = true;
            }
            catch { }
            return success;
        }
 
and GetConnection():
private /*OleDbConnection*/ SqlConnection GetConnection()
        {
            string myConnString = this.IntegratedSecurity ?
                String.Format("Data Source={0};Integrated Security=SSPI;Connect Timeout={1}", this, this.TimeOut)
                : String.Format("Data Source={0};User Id={1};Password={2};Connect Timeout={3}",
                this, this.UserId, this.Password, this.TimeOut);
 
            return new /*OleDbConnection*/ SqlConnection(myConnString);
        }
 
I also changed Seek method because there was this thing where test machine had two instances default and named and they were returned in same string, well it may not be the most elegant of solutions, but it works.
 
static public SqlServerInfo[] Seek()
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
 
            //socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
            //socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000);

            //  For .Net v 2.0 it's a bit simpler
            socket.EnableBroadcast = true;	// for .Net v2.0
            socket.ReceiveTimeout = 3000;	// for .Net v2.0

            ArrayList servers = new ArrayList();
            try
            {
                byte[] msg = new byte[] { 0x02 };
                IPEndPoint ep = new IPEndPoint(IPAddress.Broadcast, 1434);
                socket.SendTo(msg, ep);
 
                int cnt = 0;
                byte[] bytBuffer = new byte[1024];
                do
                {
                    cnt = socket.Receive(bytBuffer);
                    servers.Add(new SqlServerInfo(null, bytBuffer));
                    string info = System.Text.ASCIIEncoding.ASCII.GetString(bytBuffer, 3, BitConverter.ToInt16(bytBuffer, 1));
                    if (info.Contains(";;") & (info.Length > info.IndexOf(";;") + 12))
                    {
                        if (info.Substring(info.IndexOf(";;") + 2, 10).ToLowerInvariant().Equals("servername"))
                        {
 
                            info = info.Substring(info.IndexOf(";;") + 2);
                            servers.Add(new SqlServerInfo(null, info));
                        }
 
                    }
 
                    socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 300);
                } while (cnt != 0);
            }
            catch (SocketException socex)
            {
                const int WSAETIMEDOUT = 10060;		// Connection timed out. 
                const int WSAEHOSTUNREACH = 10065;	// No route to host. 

                // Re-throw if it's not a timeout.
                if (socex.ErrorCode == WSAETIMEDOUT || socex.ErrorCode == WSAEHOSTUNREACH)
                {
                    // DO nothing......
                }
                else
                {
                    //					Console.WriteLine("{0} {1}", socex.ErrorCode, socex.Message);
                    throw;
                }
            }
            finally
            {
                socket.Close();
            }
 
            // Copy from the untyped but expandable ArrayList, to a
            // type-safe but fixed array of SqlServerInfos.

            SqlServerInfo[] aServers = new SqlServerInfo[servers.Count];
            servers.CopyTo(aServers);
            return aServers;
        }
 

GeneralUse SMO to get SQL ServersmemberMember 476816314 May '08 - 22:13 
You can also use:
 
DataTable availableServers;
DataTable availableLocalServers;
 
availableServers = SmoApplication.EnumAvailableSqlServers(false); // local and Network SQL instances
availableLocalServers = SmoApplication.EnumAvailableSqlServers(true); // only local SQL instances
 
enjoy.
AnswerRe: Use SMO to get SQL ServersmemberMember 105382019 Aug '08 - 5:50 
SMO is not all fun and games, it fails to find all servers, it fails to work unless you include some dlls which are version specific, in a word: It worked slow but reliable on development machine but it had problems on some test machines, and you need to create an installer, you can't just use .exe file because of aforementioned dlls.
GeneralGot an Exception while comming in the loop second timemembernsadhasivam5 May '08 - 3:05 
Hi,
 
It identifies the local server in the first loop and when it receives in the second loop it gets an exception ofTime out,. Please help me
GeneralAlways times out, no servers respondmemberghawkes23 Mar '08 - 9:25 
Anyone else getting the issue where this times out every time? I am not getting any servers to respond. Thank you.
 
- G
 

static void Main(string[] args)
{
// Many thanks to CodeProject and James Curran
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.EnableBroadcast = true;
socket.ReceiveTimeout = 30000;
 
List&lt;string&gt; servers = new List&lt;string&gt;();
 
try
{
byte[] msg = new byte[] { 0x02 };
IPEndPoint ep = new IPEndPoint(IPAddress.Broadcast, 1434);
socket.SendTo(msg, ep);
 
int count = 0;
byte[] buffer = new byte[1024];
do
{
count = socket.Receive(buffer);
Console.WriteLine(count);
Console.WriteLine(buffer.ToString());
socket.ReceiveTimeout = 30000;
} while (count != 0);
 
}
catch(SocketException ex)
{
const int WSAETIMEDOUT = 10060; // Connection timed out.
const int WSAEHOSTUNREACH = 10065; // No route to host.
Console.WriteLine("ErrorCode: " + ex.ErrorCode);
// Re-throw if it's not a defined exception
if (ex.ErrorCode == WSAETIMEDOUT || ex.ErrorCode == WSAEHOSTUNREACH)
{
// DO nothing......
}
else
{
throw;
}
}
finally
{
socket.Close();
}

Console.ReadLine();
}
GeneralRe: Always times out, no servers respondmembernsadhasivam5 May '08 - 3:46 
hey
 
me too got time out exception. But not in the first time. The loop executes once. but it goes for second time it throws an exception. Any solution please!
GeneralRe: Always times out, no servers respondmemberNat181018 Feb '09 - 22:32 
I had the same issue, I was working from home at the time but once I was back in the office it worked, it was down to the vpn software blocking the port I needed, so you might want to check that nothing is blocking that port i.e. vpn, fireware etc.
GeneralDoesn't return if the network is disabledmemberarupk_das_200212 Nov '07 - 17:28 
Hi
I am facing a problem, the code doesn't return anything if the network is disabled, though there are local instances. Confused | :confused:
GeneralGet list of all the terminalsmemberMathur Preeti9 Feb '07 - 19:21 
Can you help me to get the list of all the terminals connected into the network..
 
I want all the hosts connected to the network.
 
Thank you in advance.
 
-Preeti
QuestionHow about WMImembersmch25 Jan '07 - 22:15 
Very good article.
As I understand, WMI is meant to deliver exactly this kind information.
 
I was just curious to know if you have experience in using WMI for such purposes, and its pros & cons?
 

 
Shrirang
AnswerRe: How about WMImemberJames Curran26 Jan '07 - 2:19 
I'm not an expert on WMI, but as I understand it, you would have to get a list of computers on the network, and then use WMI to ask each indivdually if they had an sql server running.
 
Truth,
James

Generaldetecting named msde instancesmemberrajeshatsrin22 Aug '06 - 20:52 
Hi James
Great arcticle.
I have one doubt about fetching the list of running msde instances through c#.
I have 3 instances of MSDE installed on my machine say for example
MACHINE11\DBSTORAGE1
MACHINE11\DBSTORAGE2
MACHINE11\DBSTORAGE3
 
Now through c# using SMO or SQL DMO when I retreive the list of sql server instances, I get only the default instance. How can I get the list of named instances like above.
Can you pls help me with this.
 
Rajesh

QuestionTCP disabled?membermcm_xyz122 Aug '06 - 3:51 
The code is great.
 
The drawback is that it does not discover SQL Servers not having TCP protocol enabled...
Are there any other known methods for finding them? (note that sql service manager sees them)...
GeneralUsing Registry [modified]memberBahadir Cambel7 Jun '06 - 3:12 
How about to use registry to look up last connected servers in Sql Server Enterprise Manager ?
 
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SuperSocketNetLib\LastConnect
 
Left Side of the "||" represents name section
Right Side of the "||" represents data section
There are 4 entries in the registry field ;
NAME || DATA
------ --------
1-(local) || -33423352:lpc:BADOLAPTOP
-----------------------------------------------
2-BADOLAPTOP || -33423352:lpc:BADOLAPTOP - Local MSDE database with machine name(so its servername is default)
-----------------------------------------------
3-MUHASEBE\LOKAL || -150536184:tcp:MUHASEBE,1433 - Remote DB with machine name (MUHASEBE) and "LOKAL" = SERVER NAME
------------------------------------------------
4-BADOLAPTOP\TEKNOIN || -150536184:lpc:BADOLAPTOP\TEKNOIN - Local DB with machine name (BADOLAPTOP) AND Server Name (TEKNOIN)
------------------------------------------------
 
So , could it take us any easy solution ?
(Frankly , I'm too lazy to implement any solution.If somebody is interested , please let me know.)
Imo , there could be some security issues regarding to read the reg
 
Bado.
 
-- modified at 9:16 Wednesday 7th June, 2006
GeneralULTIMATE! Many thanks!memberXTonos29 May '06 - 23:33 
I didnt want to use SQLDMO and couldnt use .net2.0 SqlDataSourceEnumeratorm so I was using SQLInfoEnumerator. Unfortunately it was unable to detect some specific servers (even if there were only 4 servers totally), so I was thinking about broadcasting udp manually, but too lazy to sniff with Ethereal Smile | :)
Many thanks!!!
 
Tonos
GeneralTimeout problemmemberKnut Morten Ovesen19 Feb '06 - 21:20 
I always time out after 3000 ms no mater what i set as timeout. Why??
 
I have tried this in VS2003 and VS2005..
 

socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 100000);
 
// For .Net v 2.0 it's a bit simpler
//socket.EnableBroadcast = true; // for .Net v2.0
//socket.ReceiveTimeout = 1000000; // for .Net v2.0

 

GeneralRe: Timeout problemmemberKnut Morten Ovesen24 Feb '06 - 0:51 
This does excactly the same but using UdpClient. Where I dont get the Timeout excetion. But i sill finds only 71 of the 136 sql servers on the network.. Smile | :)

public static ArrayList UdpClient()
{
UdpClient client = new UdpClient();
byte[] msg = new byte[] { 0x02 };
 
client.MulticastLoopback = true;
client.EnableBroadcast = true;
client.DontFragment = true;

IPEndPoint serverEndpoint = new IPEndPoint(IPAddress.Broadcast,1434);
client.Send(msg, msg.Length, serverEndpoint);
 
ArrayList lst = new ArrayList();
 
while(client.Available>0)
{
IPEndPoint from = new IPEndPoint(IPAddress.Any,0);
byte[] revc = client.Receive(ref from);
String data = System.Text.ASCIIEncoding.ASCII.GetString(revc, 3, BitConverter.ToInt16(revc, 1));
lst.Add(data);
}
client.Close();
return lst;
}

GeneralRe: Timeout problemmemberScott S.2 Mar '06 - 2:30 
Has anyone considered putting this "scan" for instances on a background thread that could watch for replys for an extended length of time. The timeout could then be set to anyting and the Receive would just be re-called over and over until you purposely tell the thread to stop looking.
 
As instances are found the thread would update it's list. It could hold a delegate or send events to notify interested code anytime a new instance was found. That way you could keep adding to a listbox/combobox as instances come in, but not stop the user from picking one if it is already found.
 
Better still kick off the search when the app first comes up, so that most if not all instances have been found before the user ever gets to the point where they need to pick.
 
This idea is almost a service held internally, but could be implemented as a real, though small, service where your app could use remoting calls to ask it for the list of instances.
 
I'm a bit busy on another project right now, but when I get back to the one that uses SQLServer lists I think I'll put some more thought into this and actually try something like this idea.
GeneralA little enhancementmembercarlopagliei10 Feb '06 - 3:53 
Hi, first of all thanx for your excellent work.
I've found that the reply parser is not able to detect multiple instances of sql server on the same machine.
I've modified your Seek procedure as follows below so that now is able to detect multiple instances.
Thanx again for your work.
 

static public SqlServerInfo[] DiscoveryAvailableServers() {
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
 
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000);
 
// For .Net v 2.0 it's a bit simpler
// socket.EnableBroadcast = true; // for .Net v2.0
// socket.ReceiveTimeout = 3000; // for .Net v2.0
 
ArrayList servers = new ArrayList();
try {
byte[] msg = new byte[] { 0x02 };
IPEndPoint ep = new IPEndPoint(IPAddress.Broadcast, 1434);
socket.SendTo(msg, ep);
 
int cnt = 0;
byte[] bytBuffer = new byte[1024];
do {
cnt = socket.Receive(bytBuffer);
// *** MODIFIED ************************************************
string reply = ASCIIEncoding.ASCII.GetString(bytBuffer, 3, BitConverter.ToInt16(bytBuffer, 1));
int p;
int startIndex = 0;
do {
p = reply.IndexOf(";;", startIndex);
if (p > startIndex) {
servers.Add(new SqlServerInfo(null, reply.Substring(startIndex, p - startIndex)));
startIndex = p + 2;
}
} while (p >= 0);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 300);
} while (cnt != 0);
// ***************************************************
}
catch (SocketException socex) {
const int WSAETIMEDOUT = 10060; // Connection timed out.
const int WSAEHOSTUNREACH = 10065; // No route to host.
 
// Re-throw if it's not a timeout.
if (socex.ErrorCode == WSAETIMEDOUT || socex.ErrorCode == WSAEHOSTUNREACH) {
// DO nothing......
}
else {
// Console.WriteLine("{0} {1}", socex.ErrorCode, socex.Message);
throw;
}
}
finally {
socket.Close();
}
 
// Copy from the untyped but expandable ArrayList, to a
// type-safe but fixed array of SqlServerInfos.
 
SqlServerInfo[] aServers = new SqlServerInfo[servers.Count];
servers.CopyTo(aServers);
return aServers;
}
 
}
 
paco
GeneralRe: A little enhancementmemberrajeshatsrin23 Aug '06 - 2:35 
Hi Paco
 
I tried implementing your change, but it lists the sql server instance but doesn't list any of the msde instance running on my machine. It gives me this error
Login failed for user ''. The user is not associated with a trusted SQL Server
connection.
I have one doubt about fetching the list of running msde instances through c#.
I have 3 instances of MSDE installed on my machine say for example
MACHINE11\DBSTORAGE1
MACHINE11\DBSTORAGE2
MACHINE11\DBSTORAGE3
 
Now through c# using SMO or SQL DMO when I retreive the list of sql server instances, I get only the default instance. How can I get the list of named instances like above.
Can you pls help me with this.
 

Rajesh


 
Rajesh

GeneralWith VMWare it does not workmemberIhor Bobak9 Jan '06 - 9:32 
Nothing works in the case if VMWare vitrual network card is enabled. If it is disabled - everything works fine.
 
Who has the same problem and knows how to solve it?
QuestionHow do you enumerate servers that the SQL Browser service is disabled?membermheschl5 Jan '06 - 11:05 
How do you enumerate servers that the SQL Browser service is disabled?
 
When you run osql /L from the command prompt, you retrieve a list of servers that have the SQL Browser service set to enabled and disabled. Anyway to retrieve the complete list of servers (as per osql /L) using C# and without using odbc32.dll.
 
Mark H
GeneralI get 3 instances of the same server instead of onememberGeorge Marselis4 Dec '05 - 3:21 
Hey guys, I run msde2000 on the local lan and i keep getting 3 instances of the same database. what gives?
 
does MSDE2005 respond the same way?
GeneralOracle &amp; MySQLmemberGeorge Marselis3 Dec '05 - 11:59 
is there something similar i coud use to discover oracle servers on the local network?
 
I'm sure it's a broadcast packet, again, i just don't have money right now to shell for a new computer to turn into an oracle box.
 
And as far as doing the same thing for MySQL, well, i'm sorry, guys, you can't Smile | :) at least not with the standard version of mysql. hmmmm good idea, an article about a MySQL heartbeat monitor in C# that responds to broadcast packets.
 
-><-
There is no spoon
GeneralDoesn't find all server instancesmemberScott S.2 Dec '05 - 7:04 
I've been looking for a faster way to get the list of SQL Server for some time now, and I thought this was it. I'd tried to do this before, but didn't have a sniffer to figure it all out.
 
I implemented portions of your code to just pull out the list of the server names ... I don't need the rest in my app.
Your routines returned 4 at first, but 3 were identical. I discovered the identical ones were because my machine has VMWare Workstation installed so it has virtual networks all multi-homed to my NIC, so it reported my machine 3 times. I added code to weed out duplicates, so it then returned just 2 servers.
 
One minor complaint is it returns the machine name for the local server instead of "(local)".
 
The real problem is my current routine returns 4 different machines having servers. I tried changing the timeouts in your code all the way up to 10s, but it never found the others.
 
So I'm still using my existing calls for now ... which FYI once used SQLDMO, but quite some time ago I "upgraded" them to use ODBC because the ODBC DLLs ship with windows, and SQLDMO isn't always there, not to mention how slow it was.
 
For your reference the 2 machines not found on my network are both MSDE installations, that I can connect to and use with my app, and my current functions find, though they take a fair bit longer than yours. I'll include my current code below if you'd like to try it out and see if you can "sniff" what more it might be doing to find the opther two machines.
 
-- code below --
public static string[] MicrosoftSQLServers()
{
// This uses SQLDMO, which is a pig, must be installed
// onto the machine, and has to be called through Interop
// services because it is COM, so is slower yet!
//SQLDMO.Application sqlApp = new SQLDMO.ApplicationClass();
//SQLDMO.NameList sqlSvrs = sqlApp.ListAvailableSQLServers();
//sqlApp = null;
//
//string[] sqlServers = new string[sqlSvrs.Count];
//for( int srvNum=0; srvNum
/// Enumerate for SQLServer/Databases based on the passed information it the string
/// The more information provided to SQLBrowseConnect the more granular it gets so
/// if only DRIVER=SQL SERVER passed then a list of all SQL Servers is returned
/// If DRIVER=SQL SERVER;Server=ServerName is passed then a list of all Databases on the
/// servers is returned etc
///
/// A valid string to query for
///
private static string[] RetrieveInformation( string InputParam )
{
IntPtr environmentHandle = IntPtr.Zero;
IntPtr connectionHandle = IntPtr.Zero;
StringBuilder inConnection = new StringBuilder( InputParam );
short stringLength= (short)inConnection.Length;
StringBuilder outConnection = new StringBuilder( DEFAULT_RESULT_SIZE );
short stringLength2Ptr= 0;

try
{
if( SQL_SUCCESS == SQLAllocHandle( SQL_HANDLE_ENV, environmentHandle, out
 
environmentHandle ) )
{
if( SQL_SUCCESS == SQLSetEnvAttr( environmentHandle,
 
SQL_ATTR_ODBC_VERSION, (IntPtr)SQL_OV_ODBC3, 0 ) )
{
if( SQL_SUCCESS == SQLAllocHandle( SQL_HANDLE_DBC,
 
environmentHandle, out connectionHandle ) )
{
if( SQL_NEED_DATA == SQLBrowseConnect(
 
connectionHandle, inConnection, stringLength, outConnection, DEFAULT_RESULT_SIZE, out stringLength2Ptr ) )
{
if( SQL_NEED_DATA != SQLBrowseConnect(
 
connectionHandle, inConnection, stringLength, outConnection, DEFAULT_RESULT_SIZE, out stringLength2Ptr ) )
{
return( null );
}
}
}
}
}
}
catch
{
return( null );
}
finally
{
FreeConnection( connectionHandle );
FreeConnection( environmentHandle );
}
if( outConnection.ToString() != "" )
return( ParseSQLOutConnection( outConnection.ToString() ) );
return( null );
}
///
/// Parse an outConnection string returned from SQLBrowseConnect
///

/// string to parse
///
private static string[] ParseSQLOutConnection(string outConnection)
{
int m_Start = outConnection.IndexOf(START_STR) + 1;
int m_lenString = outConnection.IndexOf(END_STR) - m_Start;
if((m_Start>0) &&(m_lenString>0))
{outConnection = outConnection.Substring(m_Start,m_lenString);}
else
{ outConnection = string.Empty;}
return outConnection.Split(",".ToCharArray());
}
private static void FreeConnection(IntPtr handleToFree)
{
if(handleToFree!= IntPtr.Zero)
SQLFreeHandle(SQL_HANDLE_DBC,handleToFree);
}
#endregion
#endregion
 

GeneralRe: Doesn't find all server instancesmemberScott S.2 Dec '05 - 7:23 
Another interesting tidbit ... my local MSDE installation is on TCP port 1434 instead of the default for SQLServer (TCP port 1433) ... could a differnt port throw off the seek?
GeneralRe: Doesn't find all server instancesmemberDavid Bradford1 Mar '06 - 14:55 
I tested the version of the class which I downloaded in a large corporate network environment. Using the osql /L command I got a list of nearly 100 SQL Servers. Usint this code the most I ever got was 3. The issue seems to be that the Receive() method times out on the 4th server. The question I have is how can I ever get around this failure? We issue a broadcast message presumably to all the SQL Servers on the network, and when the Receive() method times out on one of them, we're hosed? Anyone have another idea?
 
David Bradford
GeneralRe: Doesn't find all server instancesmemberScott S.2 Mar '06 - 2:19 
It's not clear to me from your reply if is was my code in my note or this project page's code that only returned 3 instances ... I don't have a Receive() method.
 
As far as other ideas go I have heard, but haven't been able to verify, that each instance of SQL Server will discover the others and that you can theoritically ask any instance that you can get to for a list of all those it knows about. I assume that would be fast, and as good a list as possible assuming the server you ask has been up for some time, which it normally would be in a production environment. Anyone ever see code to do this?
 
Another method that I used in the past was to ask the local system for the registered SQLServer instances. It only returns a list of instances that have been connected to at some time in the past, but it returns the list nearly instantly. It worked for me for a while because most people repeatedly use the same instances all the time, but eventually I had customers that were using my app that had never used anything to access SQL data before, and so they didn't know what to do to get to it ... ever since I've been implementing active looking for instances. Maybe a combination of these methods would be better yet.
GeneralMultiple SQL Server instances on the same machinememberCHAPEL2 Dec '05 - 0:05 
I have a machine with two SQL Server instances (CORE and CORE\DEV) but the code only discovers one. Or at least it shows only one.
I think the bug may be in the parsing of the info sent by the machine:
 
ServerName;CORE;InstanceName;MSSQLSERVER;IsClustered;No;Version;
8.00.194;tcp;1433;;ServerName;CORE;InstanceName;DEV;IsClustered;No;Version;
8.00.194;np;\\CORE\pipe\MSSQL$DEV\sql\query;tcp;1533;;

 
You can see there are two instances here separated by two semicolons.
 
Thanks
 
-- modified at 6:05 Friday 2nd December, 2005
QuestionIs this the same result as DataSourceEnumerator?memberSamJost29 Nov '05 - 21:29 
Just out of curiosity, do you get the same server list as from this piece of source (.NET 2.0)?:
 
DbProviderFactory factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
if (factory.CanCreateDataSourceEnumerator)
{
foreach (DataRow row in factory.CreateDataSourceEnumerator().GetDataSources().Rows)
{
System.Diagnostics.Trace.WriteLine(row["ServerName"] + " " + row["InstanceName"]);
}
}

AnswerRe: Is this the same result as DataSourceEnumerator?memberKnut Morten Ovesen19 Feb '06 - 21:53 
No, If you have many Sql servers on the network, like 60+ then this list is the most complete. This are the ways i have tested to recive servers.
 
1. The code in the article, times out after 3 secs no mater what. Which give you time ro recieve aprox 58 servers. (Yes i have changesd the timeout, but it dont work)
2. The ODBC retive of servers gives me an list, WHITOUT the instance names. and without some servers
3. This 2.0 code gives me the complete list With many of the instance names. Still some instancename is missing. Looks like all servers on the network is recieved here.
 
So far it liks like the last method is working best. To bad its only on .Net 2.0

GeneralFantastic!memberMartin Robins29 Nov '05 - 10:23 
What a fantastic bit of code. I have always hated having to code around SQL-DMO to acheive this and never realised it was so simple.Big Grin | :-D
 
One suggestion; since we know that we are dealing with SQL Server; how about returning an SqlConnection object from GetConnection(...) instead of an OleDbConnection?
 
Person martin = new Person();
while (martin.Alive) {
martin.Code();
}
GeneralRe: Fantastic!memberJames Curran29 Nov '05 - 16:47 
Because I need to call GetOleDbSchemaTable which is only defined in OleDbConnection.
 
When I create a .Net v2.0 version of this, I'll use the new GetSchema method, and then I return a SqlConnection (and make the method public)
 
Truth,
James
GeneralRe: Fantastic!memberMartin Robins1 Dec '05 - 8:55 
That is a fair point!
 
Person martin = new Person();
while (martin.Alive) {
martin.Code();
}
GeneralRe: Fantastic!memberScott S.2 Dec '05 - 7:14 
You don't _need_ to call GetOleDbSchema ...
 
To get the list of databases you can do this:
SqlCommand command = m_Connection.CreateCommand();
command.CommandText = "SELECT name FROM sysdatabases";
SqlDataReader dr = command.ExecuteReader();
ArrayList dbList = new ArrayList();
while( dr.Read() ) dbList.Add( dr.GetString(0) );
command.Dispose();
 
To get tables, columns, etc. use a query like this:
"SELECT * FROM Information_Schema.Tables WHERE Table_Type = 'BASE TABLE' OR Table_Type = 'VIEW'"
 
Just research 'Information_Schema' for details on how to use it! Smile | :)
GeneralNice Workmemberperlmunger29 Nov '05 - 3:24 
James,
 
This looks great. It's interesting to see how this is done from the ground up. Nice job!
 
I was wondering if you've found this method of SQL Server discovery to provide an added benefit over using the SQLDMO COM object to locate servers on the network.
 
Thanks.
 
-Matt
 
------------------------------------------
 
The 3 great virtues of a programmer:
Laziness, Impatience, and Hubris.
--Larry Wall
GeneralRe: Nice WorkmemberJames Curran29 Nov '05 - 16:51 
Basically, just that we don't have to have the COM object around. You either have to cross your fingers and hope it's already installed, or install it yourself, and worry about registration and licensing etc.
 
Truth,
James

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 24 Nov 2005
Article Copyright 2005 by James Curran
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid