Too many connections block the internet traffic

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

Too many connections block the internet traffic

Postby mark_c » Sat Mar 07, 2020 8:09 am

I am doing tests to understand how many TCP connections can manage windows simultaneously, and I realized that after a certain number of connections, but I have not yet understood how many, if you try to connect to the internet with a browser network traffic everything is blocked.

So I was looking for a function that warned me before this problem arose: how do you do it?

Thank you

Code: Select all
void __fastcall TForm1::Button5Click(TObject *Sender)
{
   AnsiString myadr;

   for(ip = 16843009; ip < 16843009 + 50000; ip++)
   {
      try
      {
         myadr=myip(ip); // 1.1.1.1 ...... 254.254.254.254

         ClientSocket1->Address = myadr;
         ClientSocket1->Port = 80;
         ClientSocket1->ClientType = ctNonBlocking;

         ClientSocket1->Open();

         ClientSocket1->Close();

      } catch(...){ }
   }

   Button5->Caption="finished";
}
mark_c
BCBJ Master
BCBJ Master
 
Posts: 234
Joined: Thu Jun 21, 2012 1:13 am

Re: Too many connections block the internet traffic

Postby mark_c » Mon Mar 09, 2020 1:15 pm

sorry but in the meantime I discovered that the cause of the block is still the error 10055 despite always closing every socket
mark_c
BCBJ Master
BCBJ Master
 
Posts: 234
Joined: Thu Jun 21, 2012 1:13 am

Re: Too many connections block the internet traffic

Postby rlebeau » Mon Mar 09, 2020 2:55 pm

mark_c wrote:I am doing tests to understand how many TCP connections can manage windows simultaneously, and I realized that after a certain number of connections, but I have not yet understood how many, if you try to connect to the internet with a browser network traffic everything is blocked.


Your code is likely wasting available ephemeral ports, making them unavailable for your app, or other apps, to use. Running out of available ports for apps to use is commonly known as "port exhaustion".

Every time a new outbound connection is being created (such as with TClientSocket::Open()), it has to be bound to a local IP/port pair before it can then connect to a remote IP/port pair. If a client does not explicitly request a local IP/port to bind to (TClientSocket does not natively support explicit local bindings, but it is possible by calling Winsock's bind() function directly in the TClientSocket::OnConnecting event), the socket implicitly binds to a local pair on the client's behalf, using an available ephemeral port from the OS. There are a limited number of such ports available.

When a socket is closed, if it is the 1st party to close the connection (which your code implies you are), the connection goes into the TIME_WAIT state (see the WinSock state diagram). During that time, the OS holds on to the local IP/port pair even though the socket has been closed, preventing that pair from being reused by subsequent connections, unless an app explicitly requests to reuse that pair by enabling the SO_REUSEADDR option via Winsock's setsockopt() function (which your code is not doing) when creating a new socket connection.

You can use the command-line netstat tool to see if you are leaving a lot of socket connections in the TIME_WAIT state when closing your test sockets.

Also, FYI, since you are using ctNonBlocking mode, TClientSocket::Open() runs asynchronously, exiting immediately and establishing the connection in the background (I've explained that to you earlier). You are Close()'ing each socket before they have a chance to actually connect to the requested server. But, each socket is still being bound to a local IP/port pair (since you are connecting to IP addresses instead of hostnames, so there is no delay in binding the sockets before Open() exits). You should wait for the TClientSocket::OnConnect or TClientSocket::OnError(eeConnect) event to be fired before moving on to the next IP in your list. Otherwise, use ctBlocking mode instead, so that TClientSocket::Open() does not exit until the connection is fully established/failed.

mark_c wrote:sorry but in the meantime I discovered that the cause of the block is still the error 10055 despite always closing every socket


10055 is WSAENOBUFS:

No buffer space available.
An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.


That is a possible outcome when you run out of available ephemeral ports.
Last edited by rlebeau on Mon Mar 09, 2020 3:08 pm, edited 4 times in total.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1674
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: Too many connections block the internet traffic

Postby mark_c » Tue Mar 10, 2020 3:47 am

thanks Remy, if I understand correctly the problem I ask myself: is there a way to tell the SO to forget the port and address when I decide to close the connection, maybe after 5 seconds using TClientSocket?


this code that I wrote is a kind of simulator, it behaves similarly to a blocking socket, but using a non-blocking socket.

Is it possible to stop waiting for the famous 21 seconds, perhaps by intervening on the socket options?
I know Remy which is a speech that we have already dealt with, but I would like, if possible, to keep the code as simple as possible.
If I don't get it wrong, I can't do anything using TClientSocket, right?

Code: Select all
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

AnsiString myip(__int64 ip);
__int64 ip;
int done;
DWORD t1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
   AnsiString myadr;

   for(ip = 16843009; ip < 16843009 + 50000; ip++)
   {
      try
      {
         done = 1;
         t1 = GetTickCount();

         myadr=myip(ip); // 1.1.1.1 ...... 254.254.254.254

         Button1->Caption=myadr;

         ClientSocket1->Address = myadr;
         ClientSocket1->Port = 80;
         ClientSocket1->ClientType = ctNonBlocking;

         ClientSocket1->Open();

         while(done == 1)
         {
                Application->ProcessMessages();
         }

         ClientSocket1->Close();

         Caption = GetTickCount() - t1;

      } catch(...){ }
   }

   Button1->Caption="finished";

}
//---------------------------------------------------------------------------

AnsiString myip(__int64 ip)
{
// da decimale a IP

        unsigned char bytes[4];
        AnsiString m;
        bytes[0] = ip & 0xFF;
        bytes[1] = (ip >> 8) & 0xFF;
        bytes[2] = (ip >> 16) & 0xFF;
        bytes[3] = (ip >> 24) & 0xFF;
        m.sprintf("%d.%d.%d.%d", bytes[3], bytes[2], bytes[1], bytes[0]);
        return m;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ClientSocket1Error(TObject *Sender,
      TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
{
        ErrorCode = 0;
        done = 0;
        ClientSocket1->Close();       
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ClientSocket1Disconnect(TObject *Sender,
      TCustomWinSocket *Socket)
{
        done = 0;
        ClientSocket1->Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ClientSocket1Connect(TObject *Sender,
      TCustomWinSocket *Socket)
{
        done = 0;       
}
//---------------------------------------------------------------------------


TIME_WAIT is also a problem, can't you purge it?

netstat output

Code: Select all
TCP    192.168.2.4:64740      1.1.1.1:80             TIME_WAIT
TCP    192.168.2.4:64741      1.1.1.2:80             TIME_WAIT
TCP    192.168.2.4:64742      1.1.1.3:80             TIME_WAIT
TCP    192.168.2.4:64743      1.1.1.4:80             TIME_WAIT
TCP    192.168.2.4:64744      1.1.1.5:80             TIME_WAIT
TCP    192.168.2.4:64745      1.1.1.6:80             TIME_WAIT
TCP    192.168.2.4:64746      1.1.1.7:80             TIME_WAIT
TCP    192.168.2.4:64747      1.1.1.8:80             TIME_WAIT
TCP    192.168.2.4:64748      1.1.1.9:80             TIME_WAIT
TCP    192.168.2.4:64749      1.1.1.10:80            TIME_WAIT
TCP    192.168.2.4:64750      1.1.1.11:80            TIME_WAIT
TCP    192.168.2.4:64751      1.1.1.12:80            TIME_WAIT
TCP    192.168.2.4:64752      1.1.1.13:80            TIME_WAIT
TCP    192.168.2.4:64753      1.1.1.14:80            TIME_WAIT
TCP    192.168.2.4:64754      1.1.1.15:80            TIME_WAIT
TCP    192.168.2.4:64755      1.1.1.16:80            TIME_WAIT
TCP    192.168.2.4:64756      1.1.1.17:80            TIME_WAIT
mark_c
BCBJ Master
BCBJ Master
 
Posts: 234
Joined: Thu Jun 21, 2012 1:13 am

Re: Too many connections block the internet traffic

Postby rlebeau » Tue Mar 10, 2020 7:27 pm

mark_c wrote:is there a way to tell the SO to forget the port and address when I decide to close the connection, maybe after 5 seconds using TClientSocket?


In a word, no. The TIME_WAIT state is an important feature of TCP, and its duration is dictated by the TCP protocol and enforced by the OS. Have a look at How to avoid TIME_WAIT state after closesocket() ?

mark_c wrote:this code that I wrote is a kind of simulator, it behaves similarly to a blocking socket, but using a non-blocking socket.


The only benefit to simulating blocking logic with a non-blocking socket is to be able to use timeouts, which your code is not using AT ALL.

mark_c wrote:Is it possible to stop waiting for the famous 21 seconds, perhaps by intervening on the socket options?


I already answered that earlier. Use a timer to close the socket, or else use select() to wait on the socket until a timeout elapses.

mark_c wrote:I know Remy which is a speech that we have already dealt with


Then why do you keep bringing it up, when I have already answered you? You don't seem to be accepting the answers I give you.

mark_c wrote:but I would like, if possible, to keep the code as simple as possible.


What you are doing is not making the code simpler. And using a ProcessMessages() loop just invites more problems than it solves. At the very least, don't call ProcessMessages() blindly, use something like MsgWaitForMultipleObjects() or GetQueueStatus() in your loop to tell you when messages are actually pending before you then process them.

Personally, I would just get rid of ProcessMessages() altogether and break up the loop logic to use TClientSocket events, eg:

Code: Select all
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

AnsiString myip(__int64 ip);
__int64 CurrentIP;
DWORD t1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
   ClientSocket1->ClientType = ctNonBlocking;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
   CurrentIP = 16843009;
   NextConnect();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::NextConnect()
{
   myadr = myip(CurrentIP); // 1.1.1.1 ...... 254.254.254.254

   Button1->Caption = myadr;

   ClientSocket1->Address = myadr;
   ClientSocket1->Port = 80;

   t1 = GetTickCount();
   ClientSocket1->Open();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::DoneConnect()
{
   Caption = GetTickCount() - t1;
   if (++CurrentIP >= (16843009 + 50000))
   {
      Button1->Caption = "finished";
      return;
   }
   NextConnect();
}
//---------------------------------------------------------------------------
AnsiString myip(__int64 ip)
{
   unsigned char bytes[4];
   bytes[0] = ip & 0xFF;
   bytes[1] = (ip >> 8) & 0xFF;
   bytes[2] = (ip >> 16) & 0xFF;
   bytes[3] = (ip >> 24) & 0xFF;
   return AnsiString().sprintf("%d.%d.%d.%d", bytes[3], bytes[2], bytes[1], bytes[0]);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ClientSocket1Error(TObject *Sender,
   TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
{
   ErrorCode = 0;
   Socket->Close();
   DoneConnect();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ClientSocket1Connect(TObject *Sender,
   TCustomWinSocket *Socket)
{
   Socket->Close();
   DoneConnect();
}
//---------------------------------------------------------------------------


mark_c wrote:TIME_WAIT is also a problem, can't you purge it?


No. You have to wait it out. This is why you shouldn't be creating a lot of outgoing connections in a short amount of time to begin with. Space them out. Put some random delays in before creating new connections. Etc.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1674
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA


Return to Technical

Who is online

Users browsing this forum: No registered users and 30 guests