select() file descriptor limit

242 views
Skip to first unread message

k3xji

unread,
Sep 20, 2008, 6:39:48 AM9/20/08
to
Hi all,

I am implementing a game server in Python and has just stumbled a big
problem while stress-testing the server core. Here is the design
layout:

- Server uses non-blocking sockets and select() to multiple connection/
read/write requests at a time.
- Each time a client connects add it to global client list.
- Every client is a potential reader so add to reader list in select()

The problem is when I have 512 open connections on Windows(according
to google, UNIX may differ on this number), I keep getting select()
too many file descriptors error. Some folks suggest to drop some of
the sockets to allow new ones or so. But I cannot understand this? As
this is a game server it seems pretty silly to drop a connection just
because we have exceeded a limit of 512 clients which seems a
reasonably low number for a real-world-server.

I intend to overcome this problem by following trick:
- Before calling select(), ensure that reader list is below 512, if
not divide the client list into chunks which have 512 total sockets.
This way, you can handle the next 512 sockets on the next select()
call.

Any ideas around this problem and my suggestion?

Regards,

Peter Duniho

unread,
Sep 20, 2008, 1:36:58 PM9/20/08
to
On Sat, 20 Sep 2008 03:39:48 -0700, k3xji <sum...@gmail.com> wrote:

> [...]


> I intend to overcome this problem by following trick:
> - Before calling select(), ensure that reader list is below 512, if
> not divide the client list into chunks which have 512 total sockets.
> This way, you can handle the next 512 sockets on the next select()
> call.

If using select(), a better approach would be to dedicate a separate
thread to each group of sockets. Otherwise, you may wind up stuck on one
call to select() with one group of sockets when a call to select() with a
different group would have worked.

That said, there are other i/o notification paradigms that allow you to
handle all your sockets as a single logical group. WSAAsyncSelect can
handle tens of thousands of sockets, by assigning each socket a different
window message ID, and IOCP can handle arbitrarily many sockets with a
small number of threads.

IOCP is the most efficient, most scalable approach, but if in spite of the
number of sockets you expect the overall throughput to be relatively low,
WSAAsyncSelect could work too.

You can use Google to find a large volume of discussion of both
techniques, in this newsgroup and elsewhere.

Pete

k3xji

unread,
Sep 21, 2008, 6:37:41 AM9/21/08
to
>If using select(), a better approach would be to dedicate a separate
> thread to each group of sockets.

Of course, ok.

However, I have done my tests for the above sceanrio but the
limitation on 512 still remains. I think it is not a select limitation
it seems a socket can only accept 512 open connections on Windows. The
second time
I call select() and try to accept the connection, that connection
simply refused.

> WSAAsyncSelect can  
> handle tens of thousands of sockets, by assigning each socket a different  
> window message ID, and IOCP can handle arbitrarily many sockets with a  
> small number of threads.
> IOCP is the most efficient, most scalable approach, but if in spite of the  
> number of sockets you expect the overall throughput to be relatively low,  
> WSAAsyncSelect could work too.

I am using Python and I want the code to be really portable.
Portability
is the reason I have chosen select(). Is there any other portable io
paradigm
more efficient than select()?

-SC-

Peter Duniho

unread,
Sep 21, 2008, 2:35:22 PM9/21/08
to
On Sun, 21 Sep 2008 03:37:41 -0700, k3xji <sum...@gmail.com> wrote:

>> If using select(), a better approach would be to dedicate a separate
>> thread to each group of sockets.
>
> Of course, ok.
>
> However, I have done my tests for the above sceanrio but the
> limitation on 512 still remains. I think it is not a select limitation
> it seems a socket can only accept 512 open connections on Windows. The
> second time
> I call select() and try to accept the connection, that connection
> simply refused.

First, if you're running into a _default_ limit of "512 anything", it's
not inherent in Winsock itself. It also doesn't sounds familiar as a
Windows limitation, so I'd hazard a guess it's a limitation in Python
somehow.

Second, I don't understand your description of the limitation. If there's
a limitation of "512 anything" that you're running into, it seems like
you'd hit the limit when doing something for the 513th time, not the
second time.

By default, the maximum number of sockets you can use with select() is 64.
http://tangentsoft.net/wskfaq/advanced.html#64sockets

But that restriction has to do only with how many sockets the select()
function can watch at a time; it doesn't affect how many actual sockets or
connections can be created.

Not that I'm familiar with Python, but you may want to post some code so
that it's more clear what limitation you're dealing with here is and how
it manifests itself. Your description of it doesn't seem to be conveying
the information clearly.

Windows itself can handle hundreds of thousands of simultaneous
connections, if not millions, depending on system configuration. If you
literally cannot open more than 512 connections, that's not because of
Windows.

> I am using Python and I want the code to be really portable. Portability
> is the reason I have chosen select(). Is there any other portable io
> paradigm
> more efficient than select()?

The only other "portable" paradigm would be dedicating one thread to each
connection, and that's definitely much less efficient than select(), which
is itself not nearly as efficient as at least some of the non-portable
paradigms in Winsock (especially IOCP).

With Winsock, you will pretty much have to choose between portability and
efficiency. The two are not mutually compatible.

Pete

k3xji

unread,
Sep 22, 2008, 2:21:15 AM9/22/08
to
On Sep 21, 9:35 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com>
wrote:

> On Sun, 21 Sep 2008 03:37:41 -0700, k3xji <sum...@gmail.com> wrote:
> >> If using select(), a better approach would be to dedicate a separate
> >> thread to each group of sockets.
>
> > Of course, ok.
>
> > However, I have done my tests for the above sceanrio but the
> > limitation on 512 still remains. I think it is not a select limitation
> > it seems a socket can only accept 512 open connections on Windows. The  
> > second time
> > I call select() and try to accept the connection, that connection
> > simply refused.
>
> First, if you're running into a _default_ limit of "512 anything", it's  
> not inherent in Winsock itself.  It also doesn't sounds familiar as a  
> Windows limitation, so I'd hazard a guess it's a limitation in Python  
> somehow.
>
> Second, I don't understand your description of the limitation.  If there's  
> a limitation of "512 anything" that you're running into, it seems like  
> you'd hit the limit when doing something for the 513th time, not the  
> second time.
>
> By default, the maximum number of sockets you can use with select() is 64.http://tangentsoft.net/wskfaq/advanced.html#64sockets

>
> But that restriction has to do only with how many sockets the select()  
> function can watch at a time; it doesn't affect how many actual sockets or  
> connections can be created.

Ok, Good to gear that. Then I will focus on my tests. I may be missing
something.

I think I will change my design to have another object which is only
resnposible for accepting connections. And that object will simply be
a thread and the ServerSocket will simply be blocking. Each time a
client
attempts to connect I will distribute it to appropiate ServerThread()
which is doing the actual
I/O work with select() for the group of sockets(max 512).


> Windows itself can handle hundreds of thousands of simultaneous  
> connections, if not millions, depending on system configuration.  If you  
> literally cannot open more than 512 connections, that's not because of  
> Windows.
>
> > I am using Python and I want the code to be really portable. Portability
> > is the reason I have chosen select(). Is there any other portable io  
> > paradigm
> > more efficient than select()?
>
> The only other "portable" paradigm would be dedicating one thread to each  
> connection, and that's definitely much less efficient than select(), which  
> is itself not nearly as efficient as at least some of the non-portable  
> paradigms in Winsock (especially IOCP).
>
> With Winsock, you will pretty much have to choose between portability and  
> efficiency.  The two are not mutually compatible.

Thanks for the information.

David Schwartz

unread,
Sep 22, 2008, 3:18:58 AM9/22/08
to
On Sep 20, 3:39 am, k3xji <sum...@gmail.com> wrote:

> I am implementing a game server in Python and has just stumbled a big
> problem while stress-testing the server core. Here is the design
> layout:
>
> - Server uses non-blocking sockets and select() to multiple connection/
> read/write requests at a time.
> - Each time a client connects add it to global client list.
> - Every client is a potential reader so add to reader list in select()

[snip]


> Any ideas around this problem and my suggestion?

Windows provides 'select' as a compatability feature. It's a low-
performance option intended for clients that use a very small number
of sockets. If you're writing a Windows server, you should use one of
the many Windows native socket discovery techniques such as IOCP.

DS

k3xji

unread,
Sep 22, 2008, 12:45:07 PM9/22/08
to

I really would like to implement in IOCP, but as I stated, customers
want the server to be portable and run in many platforms without any
problem. That is why I am stuck with select. First tests indicate not
so much performance problem, but we will see. Also, the 512 file
descriptor limit is a real problem for me because I need to implement
some functionality thread-safe as I am separating clients into groups.
That will again costs more development time and much worse race
conditions.


David Schwartz

unread,
Sep 22, 2008, 3:12:38 PM9/22/08
to
On Sep 22, 9:45 am, k3xji <sum...@gmail.com> wrote:

> I really would like to implement in IOCP, but as I stated, customers
> want the server to be portable and run in many platforms without any
> problem.

Then you should implement IOCP. If you want your application to be
portable to Win32 and run without issues, you need IOCP. That's how
you do I/O on that platform.

> That is why I am stuck with select.

So you're saying that not porting your application makes it more
portable? Interesting.

> First tests indicate not
> so much performance problem, but we will see. Also, the 512 file
> descriptor limit is a real problem for me because I need to implement
> some functionality thread-safe as I am separating clients into groups.
> That will again costs more development time and much worse race
> conditions.

So, *port* your application. Portable applications are so because
they've been ported to many platforms. You are simply refusing to port
your application to Win32 and instead trying to get it to work without
having to actually port it.

The 'select' function is the UNIX way. There are several Windows ways,
depending on exactly what you're doing.

Least common denominator porting results in applications that don't
work well on any platform. Portable applications need their networking
and threading code to be platform-specific.

DS

trustee

unread,
Sep 22, 2008, 10:23:06 PM9/22/08
to
Why don't you use the Twisted framework?

k3xji

unread,
Sep 24, 2008, 7:52:14 AM9/24/08
to

> Least common denominator porting results in applications that don't
> work well on any platform. Portable applications need their networking
> and threading code to be platform-specific.

Very good advise David, you are always hitting just to right point. I
have taken your advises from the other thread I have opened in this
group while designing this server and I must admit that everyone one
of them makes sense along the way.

Whatever, just admit this: if I am going to implement it in IOCP for
Win32 and poll()(presumably) for *nix, then it will cost more
development time.

Of course, I want this server to be scalable, but this design is more
maintainable/easier to develop/and more extensible(less OS specific
knowledge required). And I think those make more sense to me than to
use the *best possible IO paradigm* on the server.

BTW, how much performance loss do you foresee between select() and
IOCP ? Do you have any rough estimate?

And thanks again.

David Schwartz

unread,
Sep 24, 2008, 11:01:17 AM9/24/08
to
On Sep 24, 4:52 am, k3xji <sum...@gmail.com> wrote:

> Whatever, just admit this: if I am going to implement it in IOCP for
> Win32 and poll()(presumably) for *nix, then it will cost more
> development time.

Making an application work on n+1 platforms will always take more time
than n platforms.

> Of course, I want this server to be scalable, but this design is more
> maintainable/easier to develop/and more extensible(less OS specific
> knowledge required). And I think those make more sense to me than to
> use the *best possible IO paradigm* on the server.

True, and you don't have to use IOCP. You can use Windows events. You
can use completion routines or completion events. Windows has a huge
array of socket discovery methods you can use.

> BTW, how much performance loss do you foresee between select() and
> IOCP ? Do you have any rough estimate?

As you saw, you run into trouble after 512 sockets. IOCP works well up
to at least 16,000. Windows events used to work well to 800. I think
it's higher now.

As for a rough estimate, it really depends how you use IOCP. But
basically, you can't use 'select'. You can't even really use 'select'
on Linux if you want any kind of performance.

In fact, both 'select' and 'poll' are disasters for applications that
have large numbers of descriptors where only a small number are
active. The problem is this -- if your application is caught up (no
data pending on any sockets because you just serviced them all), and
you have one very active socket, the thread calling 'select' or 'poll'
will have to be put on one wait queue for every socket you want to
wait for. A split-second later, when data arrives on the busy socket,
your thread will have to be taken off all those wait queues. You will
discover the one busy socket (after scanning your structure, ignoring
all the inactive ones again), only to repeat the process a split-
second later.

If you're developing a network server that requires performance, your
network I/O code should be your top priority. But you don't have to re-
develop it for every application. You can just develop it once (or
find an I/O library you like that provides high-performance on every
platform you care about) and use it forever more.

Fundamentally, Windows and UNIX are very different here. UNIX tells
you when to start an I/O. Windows tells you when they've finished.
Each approach has advantages and disadvantages, but you really need to
use the one that's best on each platform.

DS

Malachy Moses

unread,
Sep 24, 2008, 11:35:38 AM9/24/08
to
On Sep 24, 4:52 am, k3xji <sum...@gmail.com> wrote:
>
> <snip>

>
> BTW, how much performance loss do you foresee between select() and
> IOCP ? Do you have any rough estimate?
>

For a comparison of performance for the various socket I/O models
available in Windows (i.e., to compare the select-based I/O model with
WSAEventSelect, IOCP etc), see this post:

http://www.codeguru.com/forum/showthread.php?p=1182005#post1182005

The post contains an excerpt and a chart from the book "Network
Programming for Microsoft Windows, Second Edition", by Anthony Jones
and Jim Ohlund, Microsoft Press, 2002.

The comparison is for a simple echo-based server. As a consequence,
the numbers might not be completely accurate for different types of
servers, although the general trends should be reliable.

Reply all
Reply to author
Forward
0 new messages