Thread: Windows TCP Server Can't Handle >100 Clients

  1. #1
    "The Oldest Member Here" Xterria's Avatar
    Join Date
    Sep 2001
    Location
    Buffalo, NY
    Posts
    1,039

    Windows TCP Server Can't Handle >100 Clients

    Hey guys, it's been a few years since I've posted here. I've got a problem with a server that I wrote, it seems to be working just fine, the only problem is that once there is a large number of clients connected, any more are prevented from connecting. Well, perhaps not connecting, but they can't send any data to the server that the server can read within ten seconds, and consequently they're disconnected by my validation thread. With my server, once a client connects, it sends a quick string to the server that tells it that it's a connection that was meant for this server, meaning it's running the same protocol, not some random connection. Once the server see's this data, it sets the bool variable clients[x].validated to true. If for each client that is connected within ten seconds doesn't send that validation string, they get dropped. Problem is, 100% of my connections drop once the sockets get to the quadruple digits (i.e. socket 1088 rather that 202). I don't understand why this is--I need my server to be able to handle a lot of connections, obviously more than one hundered.

    Here, I'll lay the code down now..It's not the complete source for it, but it's the gist of it..
    Code:
    #include <windows.h>
    #include <winsock.h>
    #include <wininet.h>
    #include <stdio.h>
    #pragma comment (lib, "ws2_32.lib")
    
    #define _CRT_SECURE_NO_DEPRECATE 1
    #define _CRT_NONSTDC_NO_DEPRECATE 1
    struct CLIENT
    {
    	bool connected;
    	bool validated;
    	int initsent;
    	long timeconnected;
    	SOCKET sock;
    	long id;
    	char address[100];
    	char hostname[100];
    };
    fd_set master;   
    fd_set temp; 
    CLIENT clients[65535];
    void ParseInput(char buffer[5000], SOCKET socketnum);
    void Send(char buffer2[6000], SOCKET socketnum);
    int NumOfConnections = 0;
    int authid = -100;
    int something = 110;
    char *tempnig;
    char tempnig2[100];
    char initcommand[1024];
    char mastermessage[6000];
    DWORD WINAPI SendInitCommand(LPVOID lpParam)
    {
    	while(1){
    		Sleep(1000);
    		for(int g=0; g<65535; g++)
    		{
    			if(clients[g].initsent == 3)
    			{
    				Sleep(1000);
    				Send(initcommand, clients[g].sock);
    				clients[g].initsent = 4;
    			}
    		}
    	}
    	return 0;
    }
    DWORD WINAPI ValidationThread(LPVOID lpParam)
    {
    	char tempbuffer[1024];
    	while(1)
    	{
    		Sleep(10);
    		for(int g=0; g<65534; g++)
    		{
    			if(clients[g].validated == 0 && clients[g].connected) //if the client isn't validated yet
    			{
    				if(clients[g].timeconnected < (signed)(GetTickCount()-10000)) //Still hasn't validated, kill it
    				{
    					clients[g].connected = 0;
    					FD_CLR(clients[g].sock, &master); 
    					closesocket(clients[g].sock);
    					NumOfConnections--;
    					sprintf_s(tempbuffer,1024,"ATTENTION: Client %d did not Validate properly, and was disconnected!\nHostname: %s\r\n", clients[g].id, clients[g].hostname);
    					if(authid)
    						Send(tempbuffer, authid);
    				}
    			}
    		}
    	}
    	return 0;
    }
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpParam, int inCmd)
    {
    	int h=1;
    	//First set all the clients to default values
    	for(int h=0; h<65535; h++)
    	{
    		clients[h].connected = false;
    		clients[h].timeconnected = 0;
    		clients[h].validated = false;
    		clients[h].id = -1;
    	}
    	WSADATA wsaData;
    	WSAStartup(MAKEWORD(1,1), &wsaData);
    	SOCKET ssock;
    	SOCKADDR_IN  SockAddr;
    	if ((ssock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) 
    		return -1;
    	memset(&SockAddr, 0, sizeof(SockAddr));
    	SockAddr.sin_family = AF_INET;
    	SockAddr.sin_port = htons(5191);
    	SockAddr.sin_addr.s_addr = INADDR_ANY;  
    	setsockopt(ssock,SOL_SOCKET,SO_REUSEADDR, ( char * ) &h, sizeof ( h ) );
    	if (bind(ssock, (SOCKADDR *)&SockAddr, sizeof(SockAddr)) != 0) 
    		return -1;//if the port is in use
    	if (listen(ssock, SOMAXCONN) != 0) 
    		return -1;
    	SOCKADDR_IN  GuestAddr;
    	SOCKET guest;
    	int addrlen, max, i;
    	unsigned long mode = 1;
    	char buffer[4096];
    	char rBuffer[4096];
    	char *file_to_send;
    	file_to_send = "\0";
    	if (ioctlsocket(ssock,FIONBIO,&mode) == SOCKET_ERROR) 
    		return 1;
    	FD_ZERO(&master);    
    	FD_ZERO(&temp);
    	FD_SET(ssock, &master);
    	max = (int)ssock;
    	CreateThread(NULL,0,ValidationThread,NULL,0,NULL);
    	CreateThread(NULL,0,SendInitCommand,NULL,0,NULL);
    	while (1)
    	{
    		Sleep(10);
    		temp = master;
    		if (select(max+1, &temp, NULL, NULL, NULL) == SOCKET_ERROR) 
    		{
    			break;
    		}
    		for(i = 0; i <= max; i++)
    		{
    			if (FD_ISSET(i, &temp)) 
    			{ //there is somthing to do 
    				if (i == ssock) 
    				{
    
    					//there is a new connection request
    					addrlen = sizeof(GuestAddr);
    					if ((guest = accept(ssock, (SOCKADDR *)&GuestAddr,&addrlen)) == INVALID_SOCKET)  
    						continue; 
    					else 
    					{
    
    						FD_SET(guest, &master); // add to master set
    						if (guest > (unsigned)max)  
    							max = (int)guest;			
    						}*///store the clients information under this ID
    						tempnig = inet_ntoa(GuestAddr.sin_addr);
    						sprintf_s(tempnig2,100,"%s",tempnig);
    						strcpy_s(clients[guest].address,100,tempnig2);
    						clients[guest].connected = true;
    						clients[guest].timeconnected = GetTickCount();
    						clients[guest].id = (long)guest;
    						clients[guest].sock = guest;
    						NumOfConnections++;
    
    						struct hostent *remoteHost;
    						struct in_addr addr;
    						addr.s_addr = inet_addr(clients[guest].address);
    						remoteHost = gethostbyaddr((char *) &addr, 4, AF_INET);
    						if(remoteHost != NULL)
    							strcpy_s(clients[guest].hostname,100,remoteHost->h_name);
    						else
    							strcpy_s(clients[guest].hostname,100,clients[guest].address); //use IP address instead
    
    					}
    				} 
    				else
    				{
    					memset(buffer,0,sizeof(buffer));
    					memset(rBuffer,0,sizeof(rBuffer));
    					if (recv(i, buffer, sizeof(buffer), 0) <= 0)
    					{ //There's a socket error
    						closesocket(i); 
    						FD_CLR(i, &master); // remove it from the master set
    						if(i == authid) //was it the master that disconnected?
    							authid = -100;
    						clients[i].connected = 0;
    						clients[i].initsent = 45;
    						NumOfConnections--;
    
    					} 
    					else 
    					{
    	
    						ParseInput(buffer,i);				
    					} 
    				}
    			}
    		}
    	}
    
    	closesocket(ssock);
    	return 0;
    
    }
    void ParseInput(char buffer[5000], SOCKET socketnum)
    {
    	if(!strcmp(buffer, "VALIDATEME"))
    	{
    		clients[socketnum].validated = 1;
    		Send("You have been validated.\r\n",socketnum);
    		clients[socketnum].initsent = 3;
    
    	}
           return 0;
    }
    When displayed in the console, the validation thread tells me that "Client XXXX did not validate, and was disconnected! Hostname: whatever.res.rr.com", meaning that it in fact did let the client connect and could get its complete hostname information and fill out the whole client[x] array for it, just that the client itself, when it sends the validation string "VALIDATEME", the server does not receive it if there's over 100 connections or so. It only tells me they cannot be validated when there's already around 100 connections active on the server. Could this be due to some limitation in Windows, Winsock, that I'm not aware of?

    I've been racking my brains over this one, could somebody please help me out here? I need to be able to host a lot of connections using this server in a robust manner. Everything works as it should, but I simply can't keep any more than 100 or so clients without refusing any more client connections. And I know the problem is not client-side, they are all the same and do in fact send the VALIDATEME string properly and on time. It's the server that does not accept it.

    If one of the trusty experts on this site could once again bail me out, I'd really appreciate it. Thank you!
    Last edited by Xterria; 10-10-2009 at 05:35 PM. Reason: Can't leave my encryption keys in my code on a public forum >_>

  2. #2
    Registered User
    Join Date
    Dec 2006
    Location
    Canada
    Posts
    3,229
    Are you sure the problem is in your code?

    Delays are to be expected when you are handling this many concurrent connections. Are you sure your hardware (especially router if you are using one, they drop connections when they run out of memory, and low end models have little memory) is good enough?

    Where are the clients connecting from?

  3. #3
    "The Oldest Member Here" Xterria's Avatar
    Join Date
    Sep 2001
    Location
    Buffalo, NY
    Posts
    1,039
    Thank you for your response, cyberfish.

    Are you sure the problem is in your code?
    I really wish the problem wasn't with my code. But you may be correct on that, since I have no idea what the problem could be.
    Where are the clients connecting from?
    The connections themselves obviously come from external addresses. When about 100 or so connect, the validation thread begins to drop 100% of all new connections.
    Delays are to be expected when you are handling this many concurrent connections. Are you sure your hardware (especially router if you are using one, they drop connections when they run out of memory, and low end models have little memory) is good enough?
    To rule out the possibility that this could be due to routers/switches/etc, I ran the server on my own WinXP machine. The server does not take up that much memory unless bogged down with a million connections. I then ran the client program(the client that sends the VALIDATEME string) 50 times over. So far so good, 50 connections are currently on the server and responding properly. I run client.exe another 30 times. Things are still alright. I run it a few dozen more times, and boom, instead of them connecting, they drop due to lack of validation. Every single one of them past ~100 clients or so. And remember, the same thing happens when external IP's connect rather than >100 localhost clients, so you can rule out the memory running out due to too many client.exe's running at the same time--because 100 unique connections from different machines all over the country provide the same results--dropping after the socket numbers go quadruple digit.

    Please, somebody solve this, I've researched this to no end. I would really appreciate any input anybody would have. Thank you all again.

  4. #4
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675
    Not enough backlog perhaps? See Winsock Programmer's FAQ: Advanced Winsock Issues, section 4.14.

    EDIT: Nah, bad answer. Maybe you need to rethink your I/O strategy? See http://tangentsoft.net/wskfaq/articl...rategies.html:
    If your server only has to support a moderate number of connections — up to 100 or so — you may not need overlapped I/O. Overlapped I/O is not easy to program, so if you don't need its efficiencies, you can save yourself a lot of trouble by using a simpler I/O strategy.
    Last edited by rags_to_riches; 10-11-2009 at 07:48 AM.

  5. #5
    "The Oldest Member Here" Xterria's Avatar
    Join Date
    Sep 2001
    Location
    Buffalo, NY
    Posts
    1,039
    Thanks for that link rags_to_riches, but I'm not sure I can do much with it, considering the fact that when I googled "Overlapped I/O Server Windows TCP C++" in google....this thread that I just created was the first result. I find this very ironic and agitating at the same time. Honestly, is this not common knowledge to program servers that need to support a large number of clients?

    Could somebody please at least point me in a direction where I can somehow figure this out? I need some of you experts to throw me a bone or two, I'm still not getting very far. Thanks!

  6. #6
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675
    Well, searching MSDN I found this, which might be helpful.

  7. #7
    "The Oldest Member Here" Xterria's Avatar
    Join Date
    Sep 2001
    Location
    Buffalo, NY
    Posts
    1,039
    Thank you rags_to_riches, that makes all the difference. I've been coding my servers like this for a long time and haven't run into this problem before, I've actually been able to simultaneously hold over 1500 connections in the past using code similar to what I posted, and I would link the servers together to have a large protocol running. From here on in I'll be using Overlapped I/O.
    Last edited by Xterria; 10-12-2009 at 10:32 AM.

  8. #8
    "The Oldest Member Here" Xterria's Avatar
    Join Date
    Sep 2001
    Location
    Buffalo, NY
    Posts
    1,039
    Okay, sorry to bump this thread, but just in case people run into the same problem I do with this, Overlapped I/O has *absolutely nothing* to do with what was going wrong.

    See winsock.h:
    Code:
    typedef UINT_PTR        SOCKET;
    
    /*
     * Select uses arrays of SOCKETs.  These macros manipulate such
     * arrays.  FD_SETSIZE may be defined by the user before including
     * this file, but the default here should be >= 64.
     *
     * CAVEAT IMPLEMENTOR and USER: THESE MACROS AND TYPES MUST BE
     * INCLUDED IN WINSOCK.H EXACTLY AS SHOWN HERE.
     */
    #ifndef FD_SETSIZE
    #define FD_SETSIZE      64
    #endif /* FD_SETSIZE */
    
    typedef struct fd_set {
            u_int   fd_count;               /* how many are SET? */
            SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
    } fd_set;
    And there you go. I had this problem years ago with my servers like this and I simply forgot this one, simple, stupid rule--FD_SETSIZE limits the amount of file descriptors you can use on any server. This has nothing to do with the I/O, it's simply because the number of file descriptors was capped to 64 by default by winsock.h. All you have to do is #define FD_SETSIZE to a multiple of 1024 and you're all set. I can't believe I missed this.

  9. #9
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    Thread per connection runs into problems with more than about 16 connections, as you are finding out. Use a connection pool.

  10. #10
    "The Oldest Member Here" Xterria's Avatar
    Join Date
    Sep 2001
    Location
    Buffalo, NY
    Posts
    1,039
    Quote Originally Posted by abachler View Post
    Thread per connection runs into problems with more than about 16 connections, as you are finding out. Use a connection pool.
    What does "thread per connection" have to do with anything? I'm not creating a new thread for every connection, you really need to take a look at the code. I'm able to handle up to 65535 connections now, tested it over a few machines and gotten into the tens of thousands, which is more than I need.
    Last edited by Xterria; 11-02-2009 at 02:42 AM.

  11. #11
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    Perhaps you could point out the parts of your code you are having issues with, or do you expect me to read through the 90% that are irrelevant to the problem?

  12. #12
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Quote Originally Posted by abachler View Post
    Perhaps you could point out the parts of your code you are having issues with, or do you expect me to read through the 90% that are irrelevant to the problem?
    What problem? He posted to say that he fixed the problem he was having, and gave the solution in case others came across this thread down the road.
    bit∙hub [bit-huhb] n. A source and destination for information.

  13. #13
    "The Oldest Member Here" Xterria's Avatar
    Join Date
    Sep 2001
    Location
    Buffalo, NY
    Posts
    1,039
    What problem? He posted to say that he fixed the problem he was having, and gave the solution in case others came across this thread down the road.
    Exactly. abachler, you seriously need to read through the thread before replying. I'm seeing you post prematurely all over this board.

  14. #14
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675
    I try to think about that time I ran over the mailman to prevent that from happening.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Where's the EPIPE signal?
    By marc.andrysco in forum Networking/Device Communication
    Replies: 0
    Last Post: 12-23-2006, 08:04 PM
  2. Server with multiple clients
    By Thantos in forum Networking/Device Communication
    Replies: 20
    Last Post: 09-02-2003, 05:52 PM
  3. a simple C question...
    By DramaKing in forum C Programming
    Replies: 10
    Last Post: 07-28-2002, 02:04 PM
  4. socket question
    By Unregistered in forum C Programming
    Replies: 3
    Last Post: 07-19-2002, 01:54 PM
  5. g_hWndMain = hWnd;error C2065,C2440
    By Unregistered in forum C++ Programming
    Replies: 3
    Last Post: 12-09-2001, 03:36 PM