Wednesday, December 17, 2008

Slow Performance of PHP in Windows IIS

There are a few reasons that PHP runs slowly in Windows. Not because of the request delegation from IIS to PHP via FastCGI or CGI. In fact, the delegation is quite robust in FastCGI.

TCP/IP connection problem between database and web server may be a reason. Some Windows specific behaviors may be another. One of the Windows specific behaviors is the invocation of gethostbyaddr() function.

Here is the high level behavior of gethostbyaddr() function making the invocation slow:
Original
gethostbyaddr() on windows waits for WINS resolution timeout even when you disable WINS on the server.

This means that your DNS server may return "No hostname found" and windows gethostbyaddr() will just sit there, never having asked a WINS server anything, for 3 - 4 seconds. I watched it do just that with a sniffer.
Here is the root cause:
Original
Difference between gethostbyaddr and getnameinfo.

Originally posted by: Gregor The Eye

First at all, sorry for my english. It's not my native language, and, unfortunately, i havn't time to verify all text i enter.

Now, to subject. Computer may have many "names", and two main name groups of names computers have is a "DNS" names and a "NetBIOS" names.

The "NetBIOS" name is aname you assign to your computer using system->network identification->network ID. This name
is displayed in you "network neighborhood" and generally used in local networks to identify computers.

The "DNS" name is a name given to machine in internet. This name is a synonim to machine IP (becouse IP is\s not huma-friendly). "DNS" name is stored in special internet services (so-called "DNS" servers), not at the computer itself. Generally, computer can be shut off, but it's DNS name will be available and produce correct IP.

For example, if I install OS to new computer and call it "EYE", it will have a NetBIOS name "EYE", and, connecting it to my local network, i can access it in "network neighborhood". But, from the internet, i can't access to it using "EYE" name, and instead i must use it's IP - for example - 113.54.25.14. But if i need over users to access it from internet using a human-friendly name, i pay amount of money to sertain organization, and they register a name for it - for example - "www.GregorTheEye.com". Then, user can access my machine using this name, fopr example, writing:

ping www.GregorTheEye.com

or

telnet www.GregorTheEye.com

So, we have 2 names for machine - NetBIOS name and DNS name.
In my local network, following commands will be valid:

ping 113.54.25.14
ping EYE
ping www.GregorTheEye.com

Third command will be valid ONLY if my computer is conected to internet.

And from the internet, valid commands are:

ping 113.54.14
ping www.GregorTheEye.com

As you can see, NetBIOS name is not available from internet.

The main difference between NetBIOS name and DNS name is that DNS name will be available only if computer is connected to the internet and has i's name registered on it. NetBIOS name will be always available to computers that directly connected to target one.

To get DNS name, you must send a request to DNS server (it's IP is written in the system registry if you computer is connected to internet). If DNS server is unavailable, it will take default timeout time to discover this. If it's available, DNS server will return you human-friendly name of target machine if it's exist in database.

To obtain a NetBIOS name, you must send UDP packe to target machine and wait for responce. Becouse UDP is not a guaranteed-to-deliver protocol, responce may ot came, came corrupted, came many times etc. Your program must patiently wait for responce (it's not from 137 port - you must SEND UDP packet to 137 port of target machine, but responce will be returned to the port you specify. Only Win95 no OSR2 has a bug there responce always returns to port 137).

And, finally to Win32 functions -

gethostbyaddr() will first try to connect DNS server, and, if it's unavailable, will try to get NetBIOS name. So, it will get machine name one method or another in most cases.

getnameinfo() will ONLY try a DNS name, no attempts to get a NetBIOS name(). That's the difference.

If you write a program that must get names of local machines as well as names of internet machines (for example, network or port scanner), you MUST use gethostbyaddr(). If you write a program that will operate only with names of remote machines (IRC client, for example) - you MUST use getnameinfo() becouse Microsoft instructs so in lates MSDN releases.

If you have any questions - feel free to contact me. Also, if i write something wrony, i will be very thankfull if you send ma a hint.

Best regards, Gregor The Eye.
However, there is no getnameinfo() function in PHP. One of the nice feature in PHP documentation is the commenting. A commenter contributed a gethostbyaddr_timeout() function that only query the DNS server. You need to pass the DNS address (instead of using the OS specified DNS) to the function, though.

No comments: