API Design: Is Socket.Select() seriously busted?

  • Thread starter Thread starter Dilip
  • Start date Start date
D

Dilip

I don't know how I missed this piece for so long (this may also be old
news to you guys) but in this ACMQueue article, Michi Henning goes to
elaborate lengths to detail how even today designing a good API
requires a lot experience and effort by illustrating the example of
something as common place as the select() function call available in
sockets. The example focuses on the design decision surrounding the
Select() call in C#.

It comes under the heading "Bad APIs are easy" and goes on for about 4
pages about its problems.

The link to the article is here:
http://www.acmqueue.com/modules.php?name=Content&pa=showpage&pid=488&page=1

What do the experts think?
 
Dilip said:
I don't know how I missed this piece for so long (this may also be old
news to you guys) but in this ACMQueue article, Michi Henning goes to
elaborate lengths to detail how even today designing a good API
requires a lot experience and effort by illustrating the example of
something as common place as the select() function call available in
sockets. The example focuses on the design decision surrounding the
Select() call in C#.

It comes under the heading "Bad APIs are easy" and goes on for about 4
pages about its problems.

The link to the article is here:
http://www.acmqueue.com/modules.php?name=Content&pa=showpage&pid=488&page=1

What do the experts think?

Socket.Select isn't meant to be a good, modern API, it is meant to emulate
Berkely Sockets' select() function. It looks like it does that quite well.

The suggested improvements are a big step forward from select, but pale
horribly beside event-driven programming (WSAEventSelect or WSAAsyncSelect
or a completion port which in .NET means asynchronous BeginXYZ calls with a
WaitHandle or a completion callback delegate).

Overall, Socket.Select isn't meant to be the .NET way, it's meant to ease
porting code that already uses BSD select().
 
Dilip, the article does not focus on just the unfortunate design of the
select() function. It uses it as an example. The part of the article I liked
most, is where the author condemns the API forcing the developer using error
trapping for intercepting error that should not be anticipated. In .NET
Socket class the async callback function will throw an error whenever the
connection is lost. This was discussed in this NG quite a few times and the
'experts' here see nothing wrong with relying on error trapping mechanism.
The author of the article calls the API design that encourages such
practices 'perverse', and I agree. There are similar snafus in .NET Serial
class. USB-Serial converters are very common and work quite well. The
problem is, if the user pulls the USB connector out while the 'mapped' com
port was open, there is no way the application can handle this situation,
even error trapping is not a solution. Very good article, thanks for posting
the link.

Michael
 
Socket.Select isn't meant to be a good, modern API, it is meant to emulate
Berkely Sockets' select() function. It looks like it does that quite well.

Hmmm... I would disagree with that. The .NET Select() adds the
following errors on top of the (already poor) design of the C version
of select():

- It does not allow the caller to wait in definitely (in .NET 1.1)

- It does not allow the caller to set a timeout longer than 35 minutes

- It makes it difficult to distinguish whether it returns because of a
timeout or because a
socket is read because the return type is void.

- It allows the caller to pass duplicate sockets because the
parameters a lists, not sets.

- It has ambiguous input and output values because any of the socket
lists can be null or
be an empty list.

So, as far as emulating the original is concerned, I think Select()
does quite a poor job.
The suggested improvements are a big step forward from select, but pale
horribly beside event-driven programming (WSAEventSelect or WSAAsyncSelect
or a completion port which in .NET means asynchronous BeginXYZ calls with a
WaitHandle or a completion callback delegate).

Of course the improvements I suggested are not the bees' knees of
Select() and, as I said in the article, the point wasn't to come up
with the ultimate version of select (other people have done that
already--I mentioned epoll() as an example); the point was to show how
a few seemingly small errors quickly pile up to cause a lot of grief.
Overall, Socket.Select isn't meant to be the .NET way, it's meant to ease
porting code that already uses BSD select().

Well, having done exactly that, namely ported code that already used
BSD select(), I can definitely state that .NET Select() fails to ease
porting such code quite spectacularly.

Cheers,

Michi.
 
The part of the article I liked
most, is where the author condemns the API forcing the developer using error
trapping for intercepting error that should not be anticipated.

There are tons of APIs around that throw exceptions when they
shouldn't. And, invariably, when they do, it causes grief for the
caller.
The author of the article calls the API design that encourages such
practices 'perverse', and I agree.

I don't recall using this term in that context. Not that I'd disagree
though. Asynchrnous reads from a .NET socket do exactly this: throw an
exception when for the expected outcome (namely, no data ready to
read), and return zero for the unexpected outcome (namely, connection
loss). Code that has to deal with this nonsense ends up looking very
contorted because of the mess of control flow.

Cheers,

Michi.
 
Michi,I don't recall using this term in that context.on page 5 you wrote:
(Another popular design flaw-namely, throwing exceptions for expected
outcomes-also causes inefficiencies because catching and handling exceptions
is almost always slower than testing a return value.)

This was what you wrote. What I ment, is that code like below - pasted
directly from VS2005 help for Socket class:

public static void Read_Callback(IAsyncResult ar){
StateObject so = (StateObject) ar.AsyncState;
Socket s = so.workSocket;

int read = s.EndReceive(ar);

if (read > 0) {
so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read));
s.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0,
new
AsyncCallback(Async_Send_Receive.Read_Callback), so);
}
else{
if (so.sb.Length > 1) {
//All of the data has been read, so displays it to the console
string strContent;
strContent = so.sb.ToString();
Console.WriteLine(String.Format("Read {0} byte from socket" +
"data = {1} ", strContent.Length, strContent));
}
s.Close();
}
}is guaranteed to crash when the connection is lost, or the other side
disconnects forcefully, unless the code is wrapped into try/catch. Under
Win32 Winsock there was no need for using error trapping when responding to
socket events because WSAAsyncSelect() takesthe handle of the window
receiving notifications. .NET Sockets do not offer any means to notify the
application when the socket is aboutto 'bite the dust'. So we are using
error trapping for handling predictable situations. The sad thing, it is
easy to get used to. Michael
The part of the article I liked
most, is where the author condemns the API forcing the developer using
error
trapping for intercepting error that should not be anticipated.

There are tons of APIs around that throw exceptions when they
shouldn't. And, invariably, when they do, it causes grief for the
caller.
The author of the article calls the API design that encourages such
practices 'perverse', and I agree.

I don't recall using this term in that context. Not that I'd disagree
though. Asynchrnous reads from a .NET socket do exactly this: throw an
exception when for the expected outcome (namely, no data ready to
read), and return zero for the unexpected outcome (namely, connection
loss). Code that has to deal with this nonsense ends up looking very
contorted because of the mess of control flow.

Cheers,

Michi.
 
Michi,

I don't recall using this term in that context.

on page 5 you wrote:
(Another popular design flaw-namely, throwing exceptions for expected
outcomes-also causes inefficiencies because catching and handling exceptions
is almost always slower than testing a return value.)

This was what you wrote.

Right. I didn't say "perverse" there, although I would say that's an
apt description :-)
.NET Sockets do not offer any means to notify the
application when the socket is aboutto 'bite the dust'. So we are using
error trapping for handling predictable situations. The sad thing, it is
easy to get used to.

Yes, Depressing, really. Things like this are a clear indication that
the API design never ate his own dog food...

Cheers,

Michi.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top