View Full Version : Great Mirror Code -- Not question, just sharing
djr33
04-15-2006, 09:06 AM
Well... I run a theater as part of my site that hosts independant films, and we're going to be hosting a film soon that will get thousands of downloads ("Ideality", you may have heard of it if you're into CG/filmmaking/etc.), and it'll be a big file.
As such, I decided we needed mirrors. But then I thought about the downsides.
Really... who clicks the mirror links? No one... they want to download the "real file" from the "real host"... it doesn't really save much bandwidth for the "real host".
So... the obvious option then is to only give them one option to click... a random link from the list of mirrors...
Then I realized that didn't quite work because all they'd have to do is refresh, and they might download the file twice accidentally or something, making it better, but, again, not a great solution.
So... here's a perfect answer to this:
I'll write this in a tutorial style, so you can follow along with how to add it to the page, rather than just an example:
Put the following php at the top of your page:
<?php
$ip = $_SERVER['REMOTE_ADDR']; //grabs IP address
$md5 = md5($ip); //randomizes it a bit, to be sure similar IPs are mixed up
$char = $md5[31]; //grabs the last digit of the md5 string
//(yes, it's 32 long, but since it starts at 0, the 32nd number is really 31)
$ascii = ord($char); //turn the character into it's ascii code. Ex: "c" = 99.
$n = $ascii % 10; //the % (mobius) gets the remainder from division.
//this will give you the last digit of a number. Ex: (from above) 99/10= 9r9, so... $n=9.
...
?>
Now you've got your random number setup. It's almost totally random which number an IP will get. The only way it can change is if the IP changes... so, basically, you're guaranteed, at least for a single visit, that that person will only get one number.
Note: the number will be a single digit, from 0-9.
After this, you need to setup a set of ifs (or a switch statement), like this:
<?php
...
if ($n == 0) $url = "http:/...URL0";
if ($n == 1) $url = "http:/...URL1";
if ($n == 2) $url = "http:/...URL2";
if ($n == 3) $url = "http:/...URL3";
if ($n == 4) $url = "http:/...URL4";
if ($n == 5) $url = "http:/...URL5";
if ($n == 6) $url = "http:/...URL6";
if ($n == 7) $url = "http:/...URL7";
if ($n == 8) $url = "http:/...URL8";
if ($n == 9) $url = "http:/...URL9";
...
?>
Then you've got your mirror list setup.
Just do your page as is, then do this:
<!--HTML ABOVE THIS... etc... -->
<a href="<?php echo $url; ?>">This is the mirrored link!</a>
<!--HTML below this... and no one is the wiser... looks like the "real" link... -->
There are probably others uses than just mirroring, but most of them probably relate to sorting in some way.
You could have it relate to another number than 10 URLs, too, btw... just assign multiple numbers to the same URL, or divide by 100 with the % command, and you'll have 0-99 as possible outputs, and that will be enough :)
Here's a working example.... at the moment, it just does the first part of the code, outputting the pseudorandom number that's generated.
http://ci-pro.com/misc/phptest/ipnum.php
Anyway, just thought this could be helpful to people.
Thoughts? :)
For fun, post your number... just out of curiosity
I'm 9, like I used in the example.
btw, just a note-- I'm not logging IPs or anything if you're worried... its the exact code you see above.
4.
I'm not logging IPs or anything if you're worried... its the exact code you see above.Why would anyone be worried? :)
This could fail if a user were using an anonymising proxy.
<?php
...
if ($n == 0) $url = "http:/...URL0";
if ($n == 1) $url = "http:/...URL1";
if ($n == 2) $url = "http:/...URL2";
if ($n == 3) $url = "http:/...URL3";
if ($n == 4) $url = "http:/...URL4";
if ($n == 5) $url = "http:/...URL5";
if ($n == 6) $url = "http:/...URL6";
if ($n == 7) $url = "http:/...URL7";
if ($n == 8) $url = "http:/...URL8";
if ($n == 9) $url = "http:/...URL9";
...
?>Better to use:
$url = array(
"http:/...URL0",
"http:/...URL1",
"http:/...URL2"
);
$url = $url[$n];
djr33
04-15-2006, 07:15 PM
Umm.... why would that work better?
Wouldn't it have the exact same result?
(I'm not saying it would, but I can't see a difference myself...)
Or, you could just have if 0, elseif 1, elseif 2,... else 9. That would mean it would at least go with the default...
As for IPs... some people don't like IP being logged.... or, maybe here, people don't care. Whatever... just figured I'd note that.
Wouldn't it have the exact same result?Yes, but it's less code and easier to read, and, I believe, more efficient, especially with a lot of cases.
djr33
04-15-2006, 08:11 PM
This could fail if a user were using an anonymising proxy.
I thought you substitute code was related to that sentence... nevermind.
And... I don't know if I agree with it failing. The beauty of md5 is that even with no input, it still generates a 32 character string. md5("") will give you a result... so even if they have "no IP", they'll still get a number... yeah?
Plus... even if its a fake/masked/whatever IP, it'll still have some type of IP sent to the server, yes?
Yes, but it won't have your consistency and so on.
Something else you might want to do is check that the hosts actually exist before sending the user to them:
function checkAlive($host) {
$port; $a; $f;
if(strpos($host, "://")) { // We're dealing with an URL
$a = explode($host, "://");
$protocol = $a[0];
$a = $a[1];
$a = substr($a, 0, strpos($a, "/"));
if(strpos($a, ":")) { // We've been given a port
$port = substr($a, strpos($a, ":") + 1) + 0;
$a = substr($a, 0, strpos($a, ":"));
} else
switch($protocol) {
case "http":
$port = 80;
break;
case "ftp":
$port = 21;
break;
case default:
$port = 80;
}
}
if(!$f=fsockopen($a, $port)) return false;
else fclose($f);
return true;
}Then:
for($i=$n;;$i++)
if(checkAlive($url[$i])) {
$url = $url[$i];
break;
} else if($i == $n - 1)
$url = "sorryNoMirrors.php";
else if($i == count($url) - 1)
$i = 0;That should (untested) skip to the next mirror if the chosen one is dead.
djr33
04-15-2006, 10:24 PM
Hmm.... kinda confusing. I get it.... but I'm just not used to a lot of that stuff... totally unfamiliar with ports, for example, but that's what learning is for :)
Seems like I could just assume the hosts will work... check them every day or so, or something... not as 'perfect', though.
djr33
04-16-2006, 04:26 AM
Ok, Twey. Here's a much simpler code... this work?
Full code:
<?php
$ip = $_SERVER['REMOTE_ADDR'];
$md5 = md5($ip);
$char = $md5[31];
$ascii = ord($char);
$n = $ascii % 10;
$url = array(
"http:/...URL0",
"http:/...URL1",
"http:/...URL2",
"....."
);
$mirrorsdown = 0;
for ($url = $url[$n]; !file_exists($url[$n]); $n++) {
$check++;
if ($check == 11) {
$mirrorsdown = 1;
break;
}
if ($n == 9) $n = 0;
}
if ($mirrorsdown == 1) {
$link = "Sorry, but all mirrors are down!";
}
else {
$link = "<a href=\"$url[$n]\" alt=\"file\">Download!</a>"
}
....
....
echo "...".$link."...";
?>
edit: escaped a quote :)
You put an alt on an anchor?
It may work, but not if allow_url_fopen isn't set in php.ini.
One problem with it is that you're never going to reach 11, as every time it gets to 9 it will be sent down to 0 again, so if all the mirrors are up, you're going to be in an infinite loop. Also, you've further mired yourself in that maximum of ten mirrors your original MD5-based mirror selection flung you into. It's certainly not as portable, and I think my code was actually simpler. :p
Another thing: possible values from your bit of code at the top are:
0 (0), 1 (1), 2 (2), 3 (3), 4 (4), 5 (5), 6 (6), 7 (7), 8 (8), 9 (9), 1 (a), 2 (b), 3 (c), 4 (d), 5 (e), 6 (f)
As you can see, the chances of it being 0-6 are twice those of it being 7-9. A better idea would be to use the last character of the MD5 string directly; this would allow you 16 mirrors and a fair chance on each.
djr33
04-17-2006, 03:58 AM
You put an alt on an anchor?No... hmm? That's a link with an alt tag. Ignore it if you want.
$check++;
if ($check == 11) {
$mirrorsdown = 1;
break;
}...this will reach 11. It's just a count. It'll loop 0,1,2,....,9,0 and that second 0 will be the eleventh time its run through the loop. Then the break will exit it out of the while, so it won't be endless... it'll stop right then, with "$mirrorsdown" set to 1, meaning it'll give an error, not a link.
I'm kinda lost in the last paragraph. From what I've seen, md5 doesn't just do those characters, but all letters, plus caps. I might be imagining this, though, in which case, yeah, you're right...
btw, I've used the file commands before, so I'm assuming that allow_url_fopen is working... so.... yeah.
Your code didn't really make sense to me 'cause a lot of it was complex commands I'm unfamiliar with... might be simpler to you, though. Plus, it was longer :p
Also... the only reason mine is too complex is that it does the loop to move to the next one if it's an error..... I don't really need that. Heh.
...this will reach 11. It's just a count. Yes, you're right. I apologize, I was confusing $n with $check.
btw, I've used the file commands before, so I'm assuming that allow_url_fopen is working... so.... yeah.But you decided to post it for others to use. You should always assume they have the minimal requirements when making a script public; that means no short tags, and no relying on config options that aren't always on by default.
I'm kinda lost in the last paragraph. From what I've seen, md5 doesn't just do those characters, but all letters, plus caps. I might be imagining this, though, in which case, yeah, you're right...You are. md5() returns a hexadecimal string, meaning it contains only digits from 0 to F.
Your code didn't really make sense to me 'cause a lot of it was complex commands I'm unfamiliar with...What "complex commands?" The only thing you could possibly have had trouble with was fsockopen (http://www.php.net/fsockopen)(). The rest is just simple looping and string manipulation.
Also... the only reason mine is too complex is that it does the loop to move to the next one if it's an error..... I don't really need that. Heh.So if one of your mirrors is down, all the people who would normally be sent to that mirror can't download your file?
djr33
04-17-2006, 08:04 PM
Yes, you're right. I apologize, I was confusing $n with $check.
Yeah, no problem. I added that afterwords when I realized it wouldn't get there :)
But you decided to post it for others to use. You should always assume they have the minimal requirements when making a script public; that means no short tags, and no relying on config options that aren't always on by default.Good point. I'm using this for my own stuff and sharing it as a "cool idea" more than a script to just use, but you're right to point that out... I was thinking of it for me, not for those without that command. :)
You are. md5() returns a hexadecimal string, meaning it contains only digits from 0 to F.Ah, ok. Hmm... true, then.
Your code-- I don't know much about ports, fsockopen, and things like that. It's not the syntax as much as the stuff behind it. This is all fairly new to me... trying to catch up. :)
So if one of your mirrors is down, all the people who would normally be sent to that mirror can't download your file?
Yeah... they would get a dead mirror.... so... it's true, I should code that in there. I just think I'd know which servers were up, at least in the case I'm gonna be using this code for now. But... that's a useful thing if you aren't keeping an eye on the servers.
Also, an easy alt is just to have it check, then, if it doesn't work, just revert back to a mirror that will definitely be up (maybe the host that the page itself is on). But... probly worth coding the rest.
I don't know much about ports, fsockopen, and things like that.Google for a TCP/IP tutorial.
djr33
04-17-2006, 11:04 PM
I'm not just talking about PHP with them, but the stuff in general. Anything specific I should look for? I mean, a php tut will just tell me what to type, not why.
Nor was I :)
http://compnetworking.about.com/od/tcpip/l/blfaq012.htm
There are several ports that are usually used for certain services: 80 for HTTP, 21 for FTP, 23 for telnet, 22 for SSH...
These aren't binding; it's only a convention. Thus we get URLs like http://www.example.com:8080/index.php telling the browser to connect to port 8080, not the default 80. A fairly comprehensive list of these services, with explanations, can be found in any *n?x computer's /etc/services file.
mwinter
04-18-2006, 09:49 AM
A fairly comprehensive list of these services, with explanations, can be found in any *n?x computer's /etc/services file.Or you could look at the comprehensive list of registered ports (http://www.iana.org/assignments/port-numbers) at the IANA website (http://www.iana.org/). :)
Mike
Oh, that's where it got to. Knew it was there somewhere. :p
It's not as nicely-laid-out as the /etc/services file.
Powered by vBulletin® Version 4.2.2 Copyright © 2021 vBulletin Solutions, Inc. All rights reserved.