Click to See Complete Forum and Search --> : Slow program


cbartlett
January 16th, 2008, 05:02 AM
Hello,

I've started developing a simple program to indicate whether or not computers in a certain IP range are running VNC. I've done it by simply trying to connect to the port that VNC is running on using winsock2, and outputting the results from here. The problem is, my program runs very very slowly. I expected it to be a slow program because I'm using the connect() function from winsock2. However, the speed at which it actually runs at is useless. I also suspect that it's the 4 level for loop that I had to create to go through the IP range. This does seem like a waste of CPU to me, but there's no other way I can think of, except maybe outputting them all to a text file and using that instead.

I've thought about using a separate thread for the connect() function but I still think it will run just as slow. Any ideas? Here's my source (I'd like to shamefully apologize in advance for my disgusting and inane code, I'm still learning) :


#include <cstdlib>
#include <iostream>
#include <windows.h>
#include <sstream>
#include <string>
#pragma comment(lib, "ws2_32.lib")

using namespace std;

template <class T>
inline string its(const T& t)
{
stringstream ss;
ss << t;
return ss.str();
}



int main(int argc, char *argv[])
{


SOCKET thesocket;
const int mport =5900, sport =5800;
char address[MAX_PATH];
WSAData wsaData;
thesocket=WSAStartup(MAKEWORD(2,2),&wsaData);
if(thesocket == SOCKET_ERROR)
{
cout << "Socket couldn't be started. Maybe another instance is running?\n";
WSACleanup();
return 1;
}
cout << "Socket started\n";
thesocket = socket(AF_INET,SOCK_STREAM,0);
SetConsoleTitle("VNC HUD BETA");
string ip = "";

for(int a=10;a < 11;a++)
{
for(int b =89 ;b<90;b++)
{
for(int c = 127;c<130;c++)
{
for(int d = 1;d<256;d++)

{
//ip = a + "." + b + "." + c + "." + d;
ip.append(its(a));
ip.append(".");
ip.append(its(b));
ip.append(".");
ip.append(its(c));
ip.append(".");
ip.append(its(d));

SOCKADDR_IN server;
server.sin_port = htons(mport);
server.sin_family=AF_INET;
server.sin_addr.s_addr=inet_addr(ip.c_str());
thesocket = socket(AF_INET,SOCK_STREAM,0);

if(connect(thesocket,(SOCKADDR*)(&server),sizeof(server))==SOCKET_ERROR)
{
closesocket(thesocket);
cout << ip << " isnt running vnc\n";

}
else
{
cout << ip << " is running VNC\n";
closesocket(thesocket);
}

ip = "";

}
}
}
}

closesocket(thesocket);
WSACleanup();
cin.ignore();
cin.ignore();
return 0;
}



Thank you!

MikeAThon
January 16th, 2008, 10:46 AM
Which operating system are you running? WinXP SP2 or higher?

If so, then your code is probably invoking the limitation on the number of half-open connections, which is 10. In other workds, your code is trying to connect to IP addresses that might not exist (in fact, most probably don't exist). That characteristic is often shared by malicious code, such as viral code that seeks out other computers to infect by port- and IP-scanning. If the target IP address doesn't exist, it can't send back the SYN-ACK packet that converts a half-open connection to a completed connection. The OS will only allow 10 of these half-open connections to exist at once.

To confirm, go to the system event log and see if your code is generating a bunch of 4226 events. They look like this: EventID 4226: TCP/IP has reached the security limit imposed on the number of concurrent TCP connect attempts

To view the event log, go to Start / Control Panel / Administrative Tools / Event Viewer / System. Or go to Start / Run and type eventvwr.msc Sort by Event and scrolldown to 4226.

Mike

cbartlett
January 16th, 2008, 02:04 PM
Thanks for your reply. This is on a Windows XP SP2 Pro pc. I'm pretty sure that these IP addresses do exist on our network, but to be sure I have changed the range to 10.89.129.50 to 10.89.131.255, as these are definately all used by pc's on our network. I've also changed the way it connects to the next IP by generating a text file with all the possible IP's in it, then altering this program to loop through the text file. I thought this would be faster, which it is, but only slightly.

I've also noticed that the first connection takes milliseconds, whether it is able to connect or not. However, the next connection and all those after will take at least 40-60 seconds each, regardless as to whether it can connect or not. If it was because it was not allowing more then 10 connections at once, wouldn't it still be quicker then this as I would be able to see the results of those?

Would it run faster if I some how altered the timeout of the connect function? I have read into this, but it is far beyond my scope of understanding at the moment. If not, is there another function I can use to check the port? Again I read into this, but only found connectex() (or was is exconnect()?), but once again I'm afraid I just couldn't comprehend the logic behind it.

I also looked into multithreading, but found it very complex and I'm still not sure if it would help that much.

Again, thanks for the help, it's greatly appreciated. This is doing my head in, but is dam fun(ish) :D

MikeAThon
January 16th, 2008, 02:21 PM
I'm pretty sure that these IP addresses do exist on our network, but to be sure I have changed the range to 10.89.129.50 to 10.89.131.255, as these are definately all used by pc's on our network.
In the range of 10.89.129.50 to 10.89.131.255, there are around 700 IP addresses. Are you saying that there are 700 computers on your network? Each listening for a connection on the port defined by the "mport" variable, and each ready to send a SYN-ACK to complete the connection request?

If not, then please double-check the system event log, as described above, to see if you are getting blocked by the limit of 10 on half-open connections.

I've also noticed that the first connection takes milliseconds, whether it is able to connect or not. However, the next connection and all those after will take at least 40-60 seconds each, regardless as to whether it can connect or not. If it was because it was not allowing more then 10 connections at once, wouldn't it still be quicker then this as I would be able to see the results of those?
The behavior you are seeing is completely consistent with the limitation on half-open connections. THe connections that are not being made queue up, and prevent attempts at new connections even if the newer attempts would have completed immediately.

Again, which version of Windows are you using. This is important information. If you have an older version of Windows lying around (XP SP1, Win2k, or even WIn98), try the program on the older machine and see if it works better.
Would it run faster if I some how altered the timeout of the connect function? I have read into this, but it is far beyond my scope of understanding at the moment. If not, is there another function I can use to check the port? Again I read into this, but only found connectex() (or was is exconnect()?), but once again I'm afraid I just couldn't comprehend the logic behind it.
There is no settable time-out on the connect() function. You would need to implement something by yourself, and it would involve multithreading. But, as you indicate below...
I also looked into multithreading, but found it very complex and I'm still not sure if it would help that much.

Again, thanks for the help, it's greatly appreciated. This is doing my head in, but is dam fun(ish) :D

cbartlett
January 16th, 2008, 02:28 PM
Sorry I didn't realise you meant that the PC's were online. But yeah we have about 500+ pc's and around 1500 laptops (I work in the second largest school in the UK) and thats the IP range of most of the PC's that are on all day. They all have VNC on listending on the port I defined.

I will check the system event logs whilst at work, but in the mean time I'll run it on my home pc which has WinXp SP2 home edition. I did edit my previous post to show what version I was using but I think I did it as you posted.

Thanks again for your help.

EDIT

Ok I tested the program on my home pc and it did the exact same thing. I checked the system events and it has no 4226 errors in the last few months. So if it's the fact that the program is only creating half a connection as the PC is offline, is there a faster way I can check to see if the computer is online before attempting to connect to the port, thus avoiding the wait?

Again, thank you very much for your help.

MikeAThon
January 16th, 2008, 09:55 PM
If you are not seeing the 4226 Event ID, then I don't know why the program is so excruciatingly slow. Sorry.

Incidentally, the fact that you get the same behavior both at work and at home is not surprising, since both computers are WinXP SP2. Home vs. Pro is not the issue. You really should test the program on a much older version of Windows, something that's pre-SP2.

Mike

MikeAThon
January 16th, 2008, 09:57 PM
As one suggestion for more investigations, when your program grinds to its slowness, try running "netstat -an" from a command line prompt. Post the output here. It might show something interesting.

Mike

cbartlett
January 17th, 2008, 04:07 AM
I've tried the program again at work this morning, but again it shows no events with that ID. However, it does seem quite a lot faster than yesturday. It seems to now ouput about 7-9 results in a succession with a delay between each of around 1 second. It then pauses for another 20-30 seconds and continues again. However, this is still way to slow.

Here's the results from the netstat:


C:\WINDOWS>netstat -an

Active Connections

Proto Local Address Foreign Address State
TCP 0.0.0.0:135 0.0.0.0:0 LISTENING
TCP 0.0.0.0:445 0.0.0.0:0 LISTENING
TCP 0.0.0.0:912 0.0.0.0:0 LISTENING
TCP 0.0.0.0:2967 0.0.0.0:0 LISTENING
TCP 0.0.0.0:3260 0.0.0.0:0 LISTENING
TCP 0.0.0.0:3261 0.0.0.0:0 LISTENING
TCP 0.0.0.0:5800 0.0.0.0:0 LISTENING
TCP 0.0.0.0:5900 0.0.0.0:0 LISTENING
TCP 10.89.128.195:139 0.0.0.0:0 LISTENING
TCP 10.89.128.195:1328 10.89.128.21:445 ESTABLISHED
TCP 10.89.128.195:1366 10.89.128.23:445 ESTABLISHED
TCP 10.89.128.195:1377 10.89.128.21:445 ESTABLISHED
TCP 10.89.128.195:1381 10.89.128.22:445 ESTABLISHED
TCP 10.89.128.195:1386 10.89.128.30:445 ESTABLISHED
TCP 10.89.128.195:1391 10.89.128.31:445 ESTABLISHED
TCP 10.89.128.195:1439 10.89.128.76:5900 SYN_SENT
TCP 127.0.0.1:1113 0.0.0.0:0 LISTENING
TCP 192.168.19.1:139 0.0.0.0:0 LISTENING
TCP 192.168.124.1:139 0.0.0.0:0 LISTENING
UDP 0.0.0.0:445 *:*
UDP 0.0.0.0:500 *:*
UDP 0.0.0.0:1025 *:*
UDP 0.0.0.0:1059 *:*
UDP 0.0.0.0:4500 *:*
UDP 10.89.128.195:123 *:*
UDP 10.89.128.195:137 *:*
UDP 10.89.128.195:138 *:*
UDP 10.89.128.195:1900 *:*
UDP 127.0.0.1:123 *:*
UDP 127.0.0.1:1077 *:*
UDP 127.0.0.1:1090 *:*
UDP 127.0.0.1:1099 *:*
UDP 127.0.0.1:1127 *:*
UDP 127.0.0.1:1398 *:*
UDP 127.0.0.1:1402 *:*
UDP 127.0.0.1:1900 *:*
UDP 192.168.19.1:123 *:*
UDP 192.168.19.1:137 *:*
UDP 192.168.19.1:138 *:*
UDP 192.168.19.1:1900 *:*
UDP 192.168.124.1:123 *:*
UDP 192.168.124.1:137 *:*
UDP 192.168.124.1:138 *:*
UDP 192.168.124.1:1900 *:*



I've also noticed that when they are able to connect, the results are fast. It's just the ones that are offline which slow the program down so badly.

Thanks again for your help.

MikeAThon
January 17th, 2008, 11:15 AM
OK, I just realized that I didn't read your original post carefully enough. As a result I jumped to a conclusion about half-open connections, and sent you on a wild goose chase. I apologize.

Your app is singly-threaded, with blocking sockets, so it can attempt only one connection at a time. It's slow on offline computers because the retry mechanism in TCP includes a timing back-off, as follows: if a first connection attempt doesn't succeed within 3 seconds, then a second attempt is made. For this attempt, TCP waits for 6 seconds (double) before it tries again, and doubles the waiting time once more (i.e., to 12 seconds) before it gives up and concludes that a connection cannot be made. In total, there are three connection attempts, and a total waiting time of 3+6+12=21 seconds.

Three seconds for the first attempt, and two retries (for a total of three tries), are the default values. You can change them by adjusting the following values in the registry: TcpInitialRTT and TcpMaxConnectRetransmissions. However, you should not view this as a permanent solution, since a change in these values will affect all programs running on your machine. See this thread for more information (note that the title is similar to the symptoms you see): "Winsock functions have problem returning if IP not in use" at http://www.codeguru.com/forum/showthread.php?t=424132

It might be worthwhile to change the registry values for a test, to see if it favorably affects your program's performance. Change the retries to zero and the timeout to 1 second. That way, you will be certain that you have located the source of your problem.

The long-term solution, however, is to use non-blocking sockets or multithreading. That way, you can make more than one connection attempt at one time.

Mike

PS: the following entry in netstat's output is a half-open connection
TCP 10.89.128.195:1439 10.89.128.76:5900 SYN_SENT
It means that an attempt has been made to connect to the target IP address (a SYN packet has been sent), but that the target has not yet responded with SYN-ACK. Does that make sense in your environment? In other words, is that particular IP address offline?

cbartlett
January 18th, 2008, 04:00 AM
Don't worry it's my fault for not explaining the problem clear enough.

I'll give the registry alters a try to see if it does change any thing as you suggested and post my results when I'm done.

Sorry to sound like a complete moron, but what is the non-blocking socket function called? Does the WSAAsyncSelect() function apply to me as well? As I said, I have very little ability in this whole field. I'll look into it.

And yes, that IP was down at the time. (I work in a very large school, with a very huge amount of pupils who like nothing more then to turn random computers off)

Thank you again for your help, I appreciate it.

MikeAThon
January 18th, 2008, 10:48 AM
WSAAsyncSelect() is not useful in a console application. It indeed sets the socket to non-blocking, but it then expects that your app will have a message pump, to handle the messages that will be generated.

To set a socket to non-blocking in a console app, use ioctlsocket() with the FIONBIO flag. See http://msdn2.microsoft.com/en-us/library/ms738573(VS.85).aspx

Mike