- Home /
How to find all http servers on local network at the same time?
Hi, please HELP me find a working solution for discovering all PCs on the local subnet, those serve http (on a specific port, let's say: 88 ! )
Sending http(s) requests one by one and wait 2-3000ms is not a good solution.
(Especially on 10.x.x.x )
So I guess:
- sending a Broadcast, (to 255.255.255.255)
- and wait for ALL answers in a separate thread
- while putting the results in a thread-safe list (I know how to do that)
would be the good approach for this.
But I haven't found a working solution (full code) for this yet.
So how do I create a TCPlistener or something similar under Unity to catch all http answers at the same time within 3000ms?
THANK YOU very much!
PS: I've also red here while looking at the questions similar to this, that Android-networking can be tricky? (Not implemented?)
you should be abgle to use unity's network.connect. you can just swap an internet address for a local one. depending on the router it would be something like 192.168.0.1 or 192.168.1.1 with the last number going up in order for each device connected to the router. once you got your own local IP. you could page through sequentially till you got a responce to find the others. so if your local IP was 192.168.1.1. you would check through a list like this to find connections:
192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.4
whatever the local IP address is. only the last number changes for other devices on the router:) of course the router itself usually takes the number 1. and hands out 2 for the first device and 3 for the second and so on.
you would need to create a client/host situation where you would have a port the host is listening to.
when your device starts it would look for a host. if it cant find one then it would become the host ins$$anonymous$$d of client.
of course it would be much eisier to have users just manually enter in the local ip addresses to get yourself started before you take on the more complex task of querying for it.
anyways type this into google and look at the top result "what is my local ip".
here is other link you can read: https://stackoverflow.com/questions/151231/how-do-i-get-the-local-network-ip-address-of-a-computer-programmatically-c
@toddisarockstar Yes, thank you, I know how how to:
- get the list of my network cards
- get my IP addresses
- get my subnet to calculate the IP range
... and scan manually each possible address one by one... (or doing it by creating 254 threads.)
But scanning 65000 or 16$$anonymous$$ addresses is not possible this way, because even with 250 threads simultaneously it would take ca. 13-3200 $$anonymous$$utes!
No user has so much time to wait to connect. :D
if this is just a game expected to run on a home router, you are pry not going to have 65000 devices!!!! and actually I think a standard router would only allow up to 255 local adresses? the router assigns local IPs in sequential order! so you could cap it at 20 or something.there is going to be a wait time. Ive even played games from big manufacturers that take up to 5-10 seconds or so to do this proccess. the other two ways around are having the users manually enter ip addresses.
Option three would be to have the machine report in there local host IP to a static IP or server somewhere. Then have clients grab the hosts IP from a server.
also keep in $$anonymous$$d you don't need to find all the clients!!!! you only need to find one host IP. Then you can do whatever you want from there.
Answer by Bunny83 · Jul 11, 2017 at 09:10 AM
You can't use broadcasts with the TCP protocol. TCP is a connection based protocol. It doesn't support broadcast or multicast as a TCP connection is always unicast. A TCP listener does only react to connection request packets which can't be multi / broad cast. So in order to check all possible hosts you have to test them one-by-one.
Next thing is that you can't really "test" if a TCP connection uses HTTP or if it uses some other protocol. You only know that it's TCP after you send a valid HTTP request and you got a valid HTTP response (note that "valid" doesn't necessarily mean a positive response. A 404 is also a valid response).
Finally all ports below 1025 are standardised well-known-ports. The main point is that a certain port number indicates a certain service. If an URL uses the http://
scheme it directly translates to port 80 while https://
translates to port 443. Of course it's possible for any protocol type to use any arbitrary port, but the (mis-) usage of well-known ports is not recommended. Port 88 is reserved for the kerberos authentication system.
The IP protocol supports many different encapsulated protocols where UDP and TCP are just two of them. Only UDP and TCP are using the same kind of "port number". Another very common protocol is the ICMP (Internet Control Message Protocol) which has several features the most user-known feature is an eche-request also known as ping. Commonly a ping was used to determine if there is any host at the specified IP address. However nowadays it's very unreliable as pings has been abused for DoS attacks and echo requests often are silently swallowed / discarded by a lot systems and even hardware.
Most lan discovery is done using a "listening" UDP socket on all machines that should be able to be discovered and to send a broadcast UDP packet from the discovering host. All those hosts should then respond back to the requester. However that requires all your hosts to actually run such a service.
If you just want to find any host that is serving an HTTP server on a specific port there's no way around manually trying to connect to all possible addresses. Note that a private local area network doesn't need to be a class C network. So an exhaustive search on a class B or class A network is not feasible.
We can't really recommend one method over the other without more details about your actual case. Some networks / routers / firewalls might see a systematical scan as abusive.
Dear @Bunny83 Thank you VERY VERY much for the detailed answer. :)
1.)
I didn't know that! Now I understand why I couldn't find a solution for this."You can't use broadcasts with the TCP protocol"
2.) Sorry for the port= 88 example, of course I don't use anything under 1025.
Should have wrote for example: port= 1888
3.) Yes, I know ping isn't reliable
But is there ANY possible way to find all PC available on the subnet with that specific port open via Broadcast?
... $$anonymous$$aybe via sockets?
- Tipically there would be max 2-5 PCs responding from the possible 254 IPs in local restaurants,
- but in my 10.1.x.x VPN segment ca 500 PCs from the possible 65$$anonymous$$ addresses
(because I need to overview all of them)
- so it would be $$anonymous$$UCH easier to create parallel max 2-200 TCP direct connections to check, if they are REALLY responding to my HTTP request. (Not 65000 1by1.)
But is there ANY possible way to find all PC available on the subnet with that specific port open via Broadcast?
No, if those PCs don't run a service which react to the broadcast that's not possible. In general it's not possible to "find" anything on a network. It only works if there is some sort of service / server which react to a request packet and send out a response. For example the SSDP protocol. However that only works if the PCs you want to "find" are running the SSDP service. There is a reason why port scanners exists. There is no reliable way to find all hosts on a network with a single request.
You still haven't said for what purpose you need to find all HTTP servers on the local network. I can't really imagine any use for that besides malicious or abusive intentions. If you want to find "specific" servers which run a piece of your software, it's easy. But if it's just an ordinary HTTP server you can't
Now I fully understand, thank you for the detailed description.
- $$anonymous$$y own pizza-program (written in Delph7) is running on ALL those restaurant-machines, acting as Http-servers.
- YES, it's possible to build in a secondary server function to answer on UDP as well, not just on Http/TCP ... but it would be a HUGE work
- Also I would need to set the firewall on every PC to exclude not only TCP port 1888 but UDP port 1888 too.
Why I need this? Very simple: **
When a waiter or restaurant guest is using my unity program to place an order for a pizza, it has to connect to the local network and FIND at least 1 server, independently from DHCP setting.
I don't have the time / access to set ALL PCs everywhere in the country to FIX IPs.
There are max 1 person from 100 working in a restaurant who knows about DHCP, IPs, firewalls, etc.
So the solution is to make everything fully automated and fast without any USER interaction necessary. :D
Answer by PizzaProgram · Jul 13, 2017 at 01:54 PM
I wrote a simple code to create 254 Treads to test.
But it's not working... does anyone see where's the error in this?
public static MultiThreadValues.ThreadSafeInt _found = new MultiThreadValues.ThreadSafeInt(); // how many servers responded
public static MultiThreadValues.ThreadSafeList<string> _addresses = new MultiThreadValues.ThreadSafeList<string>(); // each IP with server, who responded
btnSearchAddress.Click += delegate {
...
var task = Task.Run(() => _FIND()); // Create 254 threads to test simultaniously
task.Wait(TimeSpan.FromMilliseconds(3100));
for (int j = 0; j < _addresses.Count; j++) {
_Pzz.AddLog2List(_addresses[j]); }
}
public static void _FIND() {
_found.Value = 0;
_addresses.Clear();
string p = _Pzz._myIPstring;
if (p == "") return;
const Char _dot = '.';
StringBuilder str192 = new StringBuilder(64);
Int16 dots = 0;
for (int c = 0; c < p.Length; c++) {
if ( p[c] == _dot) dots++;
str192.Append(p[c]);
if (dots == 3) break; // found 3. "." = 192.168.1.
}
string str192168 = str192.ToString();
for (int i = 1; i < 255; i++) {
var tmp_thread = new Thread(new ThreadStart(() =>
{
var host0 = str192168 + i;
string url_txt = WebCall.CreateTestURL(host0, WebCall.PRF_.Port) ;
URL myUrl = new URL (url_txt);
bool success = false;
try {
HttpURLConnection urlc = (HttpURLConnection)myUrl.OpenConnection ();
urlc.RequestMethod = "GET"; //OR huc.setRequestMethod ("HEAD");
urlc.SetRequestProperty("Connection", "close");
urlc.ConnectTimeout = 2000;
urlc.Connect();
success = (urlc.ResponseCode == HttpStatus.Ok);
//success = (urlc.ResponseMessage == "OK");
}
catch (Exception) {}
if (success) {
_found.Add1();
_addresses.Add(host0);
}
}));
}
} //void _FIND()
Your don't start any of the threads that you create:
tmp_thread.Start();
edit
Also you don't create a local variable for your for-loop variable. The closure you create for each thread will all close over the same variable "i" and therefore will all use the same variable. This would make each thread basically use a random number.
You need to do something like this:
for (int i = 1; i < 255; i++) {
int val = i; // local variable for the closure
var tmp_thread = new Thread(new ThreadStart(() =>
{
var host0 = str192168 + val;
// ...
YES :) you are RIGHT!! I was missing the most important thing: START :D
Also many many thanks for the hint about the global/local variable "i" ... I always forget C# handles for loops as "separate procedures" and can create local variables.
(I grow up on Pascal/Delphi...)
I also started with Pascal and Delphi. However Delphi also has closures and as you can see here on SO it's actually the same situation. A closure closes over variables. Those variables can be local variables. When closing over a for loop variable each closure that you create inside the for loop will use the same variable. That means when the method is actually executed they all see the same value which is usually the last loop variable + 1.
Yes, in delphi you can't start a new scope explicitly. There you have to use seperate functions / procedures. I barely use Delphi anymore.
Do you actually test this in Unity? What's that "HttpURLConnection" class? I can't find any C# class with that name, only a Java class. Your code seems confusing.
You're right, I need to use this code both in VS2015 Xamarin AND! Unity. I will replace the HttpURLConnection part (if that's not available in Unity too) with something like this: var request = (HttpWebRequest)WebRequest.Create(url); request.Timeout = 2000; request.$$anonymous$$ethod = "HEAD"; try { using (var response = (HttpWebResponse)request.GetResponse()) { return response.StatusCode == HttpStatusCode.O$$anonymous$$;} } catch (WebException) {return false; }