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

发布于 2024-12-02 23:44:05 字数 571 浏览 2 评论 0原文

我有一个服务器应用程序。我还有一个客户端应用程序。当两个应用程序恰好位于同一网络上时,我能够在应用程序之间建立 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 的所有服务器?

I have a server application. I also have a client application. I am able to establish a tcp connection between the applications when both applications happen to be on the same network. so let's say that the computer running the server application is listening from new connections on port 2121 and it has the LAN ip address 192.168.0.120. On a different computer running the client application I will be able to establish a connection by providing port number 2121 and ip address 192.168.0.120.

Is there a way to find all computers on a network that are listening on port 2121?

One algorithm that I am thinking now is like:

  • get ip address of current computer and lets say it comes out as 192.168.0.145.

  • now most likely the server will be listening on ip addresss 192.168.0.?

  • then ping 192.168.0.1 on port 2121 then 192.168.0.2 on port 2121 ... and then keep going.

I don't know if that method is efficient. moreover there might be a possibility that the server happens to be listening on ip address 192.168.1.x

So what changes will I have to make to my server and client application so that the client is able to find all the servers listening on port 2121?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

清醇 2024-12-09 23:44:05

您提出的算法就是您需要的算法。问题之一在于候选 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 个
  • 手动设置 IP 地址(可能在笔记本电脑上)的 DHCP 服务器之外的所有服务器来修复您的基础设施。解决方案是更改为 DHCP(如果您需要始终分配给特定计算机的特定 IP,请使用静态 DHCP)

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


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

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

您可以通过找出如何减少此超时(我在分配的时间内找不到如何减少超时)或使用中提供的自定义超时来改进程序 如何配置套接字连接超时 。您还可以使用多线程并添加在线程安全集合中工作的地址并从那里使用它。

另外,您之前可以尝试 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();
    }

The algorithm you proposed is the one you need. One problem is in the dynamic generation of the candidate IP addresses.

Normally, the possible IP address range is the one given by the subnet mask ( http://en.wikipedia.org/wiki/Subnetwork ). More exactly, the part of the IP that change is that part when in the subnet mask you have 0bits (always at the end of mask).

In your example:

  • if the mask is 255.255.255.0, then your possible ip address range is
    192.168.0.*.
  • if the IP can also be 192.168.1.* then probably the mask should be 255.255.0.0
  • you can also have mask like 255.255.255.128 and the range would be 192.18.1.[1-126]. You can practically learn more using http://www.subnet-calculator.com/

The only other possibilities which cause your problem that I see to have these distinct ranges are:

  • you have more DHCP servers in your network, which is really bad as you will have "race conditions". The solution here is to fix your infrastructure by removing all but 1 DHCP server
  • you have manually set IP addresses (probably on laptops). The solution is to change to DHCP (if you need a specific IP that will always be assigned to a specific computer, use static DHCP)

Getting back to the problem of finding the problem of checking if "something" is listening on a specific port, the ICMP protocol is not the best here, as the majority of firewalls filter both the broadcast ICMP and single ICMP. If we are truly talking of a server, chances are you had to manually open the port you are looking for. Also, even if all computers respond, you still don't know if they host your wanted service.


The solution below involves computing the possible range of candidate IP addresses. After that you iterate through them to see if you can connect to your port.

In this implementation I test sequentially, which proves to be very slow as the timeout for connect is 30 seconds if the host is not on. For several hundred candidates, it doesn't sound too good. However, if the majority of host are available (even if they don't host your service), everything will go several times faster.

You can improve the program by either finding out how to decrease this timeout (I couldn't find out how in my allocated time) or to use a custom timeout as presented in How to configure socket connect timeout . You could also use multi-threading and adding the address that worked in a thread-safe collection and work with it from there.

Also, you could try pinging (ICMP) before, but you could miss valid servers.


    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();
    }
红衣飘飘貌似仙 2024-12-09 23:44:05

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

(sorry for my bad english) I am actually needing something similar to this and just found out about multicast. Here you can find an article and example. The sample app from the article worked fine on my lan. I do not know exactly how it works but maybe you can multicast something from the client and have the server(s) to respond with its IP? Or if that do not work, have the server multicasting his IP in a timed interval should do it. Sorry for the lack of informations, i just learned about this :)

寻梦旅人 2024-12-09 23:44:05

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

这个想法非常简单:应用程序的服务器可以在其中注册并且应用程序客户端可以在其中获取活动服务器列表的服务器。

  1. 服务器 A 已加载,并立即向主服务器发送一条 hello 消息
  2. 服务器 B 已加载,并向主服务器发送一条 hello 消息
  3. 服务器 A 和 B 均每隔 X 分钟向主服务器发送 hello,以便他知道它们仍在
  4. 客户端 A 上已加载 - 需要发出命令 - 向主服务器询问活动服务器列表 - 从列表中选择一个服务器 - 发出命令

需要记住的事情:

  1. 主服务器必须位于已知的地址/端口上 - 要么是固定 ip,要么是获取 ip出色地已知的 ServerName
  2. 主服务器的目的只是注册服务器并向客户端提供其地址 - 乍一看,我发现它没有其他服务可以为您的应用程序提供

如果任何服务器与您的应用程序的任何其他服务器一样好,我建议该列表是根据从该服务器收到的最后一个问候消息的时间戳进行排序 - 这样客户端将在该列表的顶部显示最有可能仍然处于运行状态的服务器(因为它报告最后处于运行状态),并且可以随后沿着列表向下移动。

此外,每次主服务器收到列表更改的问候时,因此每隔一段时间客户端请求就会获得不同的服务器列表并使用不同的优先服务器,从而减轻全面服务器的负载。

An option i am not seeing beeing discussed here is to have a Master Server.

The idea is quite simple: A server where your application's server can register and where you application clients can get a list of active servers.

  1. Server A is loaded and imediatly sends a hello message to the Master Server
  2. Server B is loaded and sends an hello message to the Master Server
  3. Both Server A and B keep sending hello's to Master Server every X Minutes so he knows they are still on
  4. Client A is loaded - Needs to issue command - asks Master Server for List of Active Servers - Picks a server from the list - issues command

Things to keep in mind:

  1. Master Server must be on a known address / port - either fixed ip or get ip throw well known ServerName
  2. Purpose of Master Server is simply register servers and supply clients with their addresses - At first glance i see no other service it could provide your application

If any server is as good as any other for your application, i would advise the list to be ordered according to timestamp of last hello message received from that server - that way client will have at the top of that list the server most likelly to still be up (since it reported beeing up last) and can go down the list subsequentially.

More over, every time the Master Server receives an hello that list changes, so every so often client requests will get a different server list and use a different preferencial server, relieving load on servers accross the board.

缺⑴份安定 2024-12-09 23:44:05

不能用和获取ip时一样的方法吗?

让客户端发送广播 - 如果没有响应请等待
服务器接收广播并用自己的 IP 发回广播。
现在客户端知道服务器在外面并且在什么IP上。

can't you use the same method as when you get your ip.

let the client send a broadcast - if no response wait
server receive broadcast and send one back with its own ip.
now the client know that the server is out there and on what ip.

给我一枪 2024-12-09 23:44:05

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

I assume you have a single server. If you can guarantee that the server location (ip address and port) is constant (or can be looked up) then each client application can 'register' with the server by connecting to it and informing the server about the ip address and local port to call back.

┾廆蒐ゝ 2024-12-09 23:44:05

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

你有两个选择。

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

  2. 让每台服务器向广播地址发送 ICMP ping,其中包含特定数据,宣布其已打开(并且可以选择不连接到客户端),但不要经常这样做(我建议测试一分钟,至少 5 分钟)生产)。您的软件所要做的就是查找广播 ping 并连接到发送 IP 地址。

更新:

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);
}

ICMP Ping does not determine if a computer is listening on a specific port, only if the computer is configured to response to a ping. ICMP is a protocol, different then TCP or UDP. It's only use for you would be to determine if an IP Address is use, and even then is becoming less viable.

You have two options.

  1. Have the client constantly check every IP address on your local network and try to open port 2121. This is not a good option.

  2. Have every server send out a ICMP ping to the broadcast address with specific data announcing it is on (and optionally not connected to a client) the never every so often (I would recommend a minute for testing, and 5 minutes minimum for production). All your software has to do is look for the broadcast ping and connect to the sending IP Address.

Update:

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);
}
国际总奸 2024-12-09 23:44:05

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

you can make the server send his location to a specific port using UDP, and the client listen to it then the client establish a connection with the server based on the given ip and port.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文