查找侦听本地网络上特定端口的服务器

时间:2011-09-02 17:32:59

标签: c# networking tcp

我有一个服务器应用程序。我也有一个客户端应用程序。当两个应用程序碰巧在同一网络上时,我能够在应用程序之间建立tcp连接。因此,假设运行服务器应用程序的计算机正在侦听端口2121上的新连接,并且它具有LAN IP地址192.168.0.120。在运行客户端应用程序的另一台计算机上,我将能够通过提供端口号2121和IP地址192.168.0.120来建立连接。

有没有办法找到正在侦听端口2121的网络上的所有计算机?

我现在想的一个算法是:

  • 获取当前计算机的IP地址,并说它出现为192.168.0.145。

  • 现在很可能服务器将侦听IP地址192.168.0。

  • 然后在端口2121上ping 192.168.0.1,然后在端口2121上ping 192.168.0.2 ...然后继续。

我不知道这种方法是否有效。此外,服务器可能正在侦听IP地址192.168.1.x

那么我必须对我的服务器和客户端应用程序进行哪些更改,以便客户端能够找到侦听端口2121的所有服务器?

7 个答案:

答案 0 :(得分:5)

您提出的算法就是您需要的算法。一个问题是动态生成候选IP地址。

通常,可能的IP地址范围是子网掩码http://en.wikipedia.org/wiki/Subnetwork)。更准确地说,改变的IP部分是在子网掩码中有0比特的部分(总是在掩码的末尾)。

在你的例子中:

  • 如果掩码是255.255.255.0,则可能的IP地址范围是 192.168.0。*。
  • 如果IP也可以是192.168。 1 。*那么掩码可能应该是255.255。 0 .0
  • 你也可以有像255.255.255.128这样的掩码,范围是192.18.1。[1-126]。您可以使用http://www.subnet-calculator.com/
  • 了解更多信息

我认为只有导致您出现问题的其他可能性具有以下不同范围:

  • 你的网络中有更多的DHCP服务器,这真的很糟糕,因为你会遇到“竞争条件”。这里的解决方案是通过删除除1个DHCP服务器之外的所有服务器来修复您的基础架构
  • 您已手动设置IP地址(可能在笔记本电脑上)。解决方案是更改为DHCP(如果您需要始终分配给特定计算机的特定IP,请使用静态DHCP)

回到发现检查“某事”是否正在侦听特定端口的问题的问题,ICMP协议在这里并不是最好的,因为大多数防火墙都过滤广播ICMP和单个ICMP。 如果我们真的在谈论服务器,那么您可能需要手动打开正在寻找的端口。此外,即使所有计算机都响应,您仍然不知道它们是否主机您想要的服务


以下解决方案涉及计算候选IP地址的可能范围。在那之后你遍历它们以查看你是否可以连接到你的端口

在这个实现中,我按顺序测试,如果主机没有打开,连接超时为30秒,证明非常慢。对于数百名候选人来说,这听起来并不太好。但是,如果大多数主机可用(即使他们没有托管您的服务),一切都会快几倍。

您可以通过了解如何减少此超时来(我无法在我的分配时间内找到)或使用自定义超时来<改进程序< / strong> How to configure socket connect timeout。您还可以使用多线程并添加在线程安全集合中工作的地址,并从那里开始使用它。

另外,您之前可以尝试ping(ICMP),但可能会错过有效的服务器


    static void Main(string[] args)
    {

        Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        int wantedPort = 21;    //this is the port you want

        byte[] msg = Encoding.ASCII.GetBytes("type msg here");


        foreach (NetworkInterface netwIntrf in NetworkInterface.GetAllNetworkInterfaces())
        {

            Console.WriteLine("Interface name: " + netwIntrf.Name);

            Console.WriteLine("Inteface working: {0}", netwIntrf.OperationalStatus == OperationalStatus.Up);

            //if the current interface doesn't have an IP, skip it
            if (! (netwIntrf.GetIPProperties().GatewayAddresses.Count > 0))
            {
                break;
            }

            //Console.WriteLine("IP Address(es):");

            //get current IP Address(es)
            foreach (UnicastIPAddressInformation uniIpInfo in netwIntrf.GetIPProperties().UnicastAddresses)
            {
                //get the subnet mask and the IP address as bytes
                byte[] subnetMask = uniIpInfo.IPv4Mask.GetAddressBytes();
                byte[] ipAddr = uniIpInfo.Address.GetAddressBytes();

                // we reverse the byte-array if we are dealing with littl endian.
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(subnetMask);
                    Array.Reverse(ipAddr);
                }

                //we convert the subnet mask as uint (just for didactic purposes (to check everything is ok now and next - use thecalculator in programmer mode)
                uint maskAsInt = BitConverter.ToUInt32(subnetMask, 0);
                //Console.WriteLine("\t subnet={0}", Convert.ToString(maskAsInt, 2));

                //we convert the ip addres as uint (just for didactic purposes (to check everything is ok now and next - use thecalculator in programmer mode)
                uint ipAsInt = BitConverter.ToUInt32(ipAddr, 0);
                //Console.WriteLine("\t ip={0}", Convert.ToString(ipAsInt, 2));

                //we negate the subnet to determine the maximum number of host possible in this subnet
                uint validHostsEndingMax = ~BitConverter.ToUInt32(subnetMask, 0);
                //Console.WriteLine("\t !subnet={0}", Convert.ToString(validHostsEndingMax, 2));

                //we convert the start of the ip addres as uint (the part that is fixed wrt the subnet mask - from here we calculate each new address by incrementing with 1 and converting to byte[] afterwards 
                uint validHostsStart = BitConverter.ToUInt32(ipAddr, 0) & BitConverter.ToUInt32(subnetMask, 0);
                //Console.WriteLine("\t IP & subnet={0}", Convert.ToString(validHostsStart, 2));

                //we increment the startIp to the number of maximum valid hosts in this subnet and for each we check the intended port (refactoring needed)
                for (uint i = 1; i <= validHostsEndingMax; i++)
                {
                    uint host = validHostsStart + i;
                    //byte[] hostAsBytes = BitConverter.GetBytes(host);
                    byte[] hostBytes = BitConverter.GetBytes(host);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(hostBytes);
                    }

                    //this is the candidate IP address in "readable format" 
                    String ipCandidate = Convert.ToString(hostBytes[0]) + "." + Convert.ToString(hostBytes[1]) + "." + Convert.ToString(hostBytes[2]) + "." + Convert.ToString(hostBytes[3]);
                    Console.WriteLine("Trying: " + ipCandidate);


                    try
                    {
                        //try to connect
                        sock.Connect(ipCandidate, wantedPort);
                        if (sock.Connected == true)  // if succesful => something is listening on this port
                        {
                            Console.WriteLine("\tIt worked at " + ipCandidate);
                            sock.Close();
                            sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        }
                        //else -. goes to exception
                     }
                    catch (SocketException ex)
                    { 
                        //TODO: if you want, do smth here
                        Console.WriteLine("\tDIDN'T work at " + ipCandidate);
                    }
                }
            }
            Console.ReadLine();
        }
        sock.Close();
    }

答案 1 :(得分:3)

(抱歉我的英语不好)我实际上需要类似于此的东西并且刚刚发现了多播。 Here你可以找到一篇文章和例子。文章中的示例应用程序在我的局域网上运行良好。我不确切知道它是如何工作的,但也许你可以从客户端多播某些东西并让服务器用它的IP进行响应?或者,如果这不起作用,让服务器多播他的IP在一个定时间隔应该这样做。抱歉缺乏信息,我刚刚了解到这一点:)

答案 2 :(得分:1)

我没有看到这里讨论的选项是拥有主服务器。

这个想法非常简单:应用程序服务器可以注册的服务器以及应用程序客户端可以获取活动服务器列表的位置。

  1. 已加载服务器A,并且imediatly向主服务器发送问候消息
  2. 加载服务器B并向主服务器发送问候消息
  3. 服务器A和服务器B每隔X分钟继续向主服务器发送问候语,因此他知道他们仍在使用
  4. 客户端A已加载 - 需要发出命令 - 向主服务器请求活动服务器列表 - 从列表中选择服务器 - 发出命令
  5. 要记住的事情:

    1. 主服务器必须位于已知地址/端口 - 固定IP或get ip throw众所周知的ServerName
    2. 主服务器的目的只是注册服务器并为客户提供地址 - 乍一看,我看不到它可以为您的应用提供其他服务
    3. 如果任何服务器与您的应用程序的任何其他服务器一样好,我建议根据从该服务器收到的上一个问候消息的时间戳来排序列表 - 这样客户端将在该列表的顶部具有服务器最多仍然可能会上升(因为它报告了最后一次)并且可以按顺序排在列表之下。

      更重要的是,每次主服务器收到列表发生变化的问候时,客户端请求都会获得不同的服务器列表,并使用不同的优先服务器,从而减轻整个服务器上的负载。

答案 3 :(得分:1)

你不能使用与获得ip时相同的方法。

让客户端发送广播 - 如果没有响应则等待 服务器接收广播并用自己的IP发回一个 现在客户端知道服务器在那里以及什么是ip。

答案 4 :(得分:0)

我假设你有一台服务器。如果您可以保证服务器位置(IP地址和端口)是常量(或可以查找),那么每个客户端应用程序都可以通过连接到服务器来“注册”服务器,并通知服务器有关IP地址和本地端口的信息。回电话。

答案 5 :(得分:0)

仅当计算机配置为响应ping时,ICMP Ping才会确定计算机是否正在侦听特定端口。 ICMP是一种协议,与TCP或UDP不同。它只是用于确定是否使用IP地址,即使这样也变得不那么可行。

您有两种选择。

  1. 让客户端不断检查本地网络上的每个IP地址,并尝试打开端口2121.这不​​是一个好的选择。

  2. 让每个服务器向广播地址发送ICMP ping,并通知特定数据(通常不会连接到客户端),这种情况从未经常发生过(我建议一分钟进行测试,5最小生产时间)。您的所有软件都要查找广播ping并连接到发送IP地址。

  3. 更新

    using System.Net.NetworkInformation;
    
    
    private Ping _Ping = new Ping();
    private PingOptions _PingOptions = new PingOptions(64, true);
    private byte[] _PingID = Encoding.ASCII.GetBytes("MyPingID");
    private _PingResponse = new AutoResetEvent(false);
    
    public <classname> //Constructor
    {
      _Ping.PingCompleted += new PingCompletedEventHander(PingCompleted);
    }
    
    public void PingCompleted(object Sender, PingCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            //Status Unknown;
        }
        else if (e.Error != null)
        {
            //Status Error;
        }
        else if (e.Reply.Status == IPStatus.Success)
        {
            // Device Replying
        }
        else
        {
            // Status Unknown
        }
    }
    
    public void StartPing(string AddressToPing)
    {
      IPAddress ipAddress = IPAddress.Parse(AddressToPing);
      _Ping.SendAsync(ipAddress, 15000, _PingID, _PingOptions, _PingResponse);
    }
    

答案 6 :(得分:0)

您可以使服务器使用UDP将其位置发送到特定端口,然后客户端监听它,然后客户端根据给定的IP和端口与服务器建立连接。