Assuming stream sockets maintain message frame
boundaries. Mind bogglingly lame.
Reason: Stream sockets (TCP) are called stream sockets, because
they provide data streams (duh). As such, the largest message
size an application can ever depend on is one-byte in length. No
more, no less. This means that with any call to send() or
recv(), the Winsock implementation may transfer any number
of bytes less than the buffer length specified.
Alternative: Whether you use a blocking or non-blocking socket,
on success you should always compare the return from send()
or recv() with the value you expected. If it is less than
you expected, you need to adjust the buffer length, and pointer,
for another function call (which may occur asynchronously,
if you are using asynchronous operation mode).
16-bit DLLs that call WSACleanup() from their
WEP. Inconceivably lame.
Reason: WEP() is lame, ergo depending on it is
lame. Seriously, 16-bit Windows did not guarantee that WEP()
would always be called, and the Windows subsystem was often in
such a hairy state that doing anything in WEP()
was dangerous.
Alternative: Stay away from WEP().
Single byte send()s and recv()s. Festering
in a pool of lameness.
Reason: Couple one-byte sends with Nagle disabled, and you
have at best a 40:1 overhead-to-data ratio. Can you say wasted
bandwidth? I thought you could.
As for one-byte receives, think of the effort and inefficiency
involved with trying to drink a Guinness Stout through a
hypodermic needle. That's about how your application would feel
"drinking" data one-byte at a time.
Alternative: Consider Postel's RFC 793 words to
live by: "Be conservative in what you do, be liberal in what
you accept from others." In other words, send modest amounts,
and receive as much as possible.
select(). Self abusively lame.
Reason: Consider the steps involved in using select(). You
need to use the macros to clear the 3 fd_sets, then
set the appropriate fd_sets for each socket, then set
the timer, then call select().
Then after select() returns with the number of sockets
that have done something, you need to go through all the
fd_sets and all the sockets using the macros to find
the event that occurred, and even then the (lack of) resolution
is such you need to infer the event from the current socket
state.
Alternative: Use asynchronous operation mode
(e.g. WSAAsyncSelect() or WSAEventSelect()).
Applications that call gethostbyname() before calling
inet_addr(). Words fail to express such all-consuming
lameness.
Reason: Some users prefer to use network addresses rather than
hostnames at times. The Winsock 1.1 specification does not
say what gethostbyname() should do with an IP address in
standard ASCII dotted IP notation. As a result, it may succeed
and do an (unnecessary) reverse-lookup, or it may fail.
Alternative: With any destination input by a user—which may
be a hostname or dotted IP address—you should call
inet_addr() first to check for an IP address, and if
that fails call gethostbyname() to try to resolve it.
Furthermore, in some applications, you may want to
explicitly check the input string for the broadcast address
"255.255.255.255," since the return value from inet_addr()
for this address is the same as SOCKET_ERROR.
Win32 applications that install blocking hooks. Grossly
lame.
Reason: Besides yielding to other applications (see item 17), blocking hook functions were
originally designed to allow concurrent processing within a
task while there was a blocking operation pending. In Win32,
there's threading.
Alternative: Use threads.
Polling with ioctlsocket(FIONREAD) on a stream
socket until a complete "message" arrives. Exceeds the bounds
of earthly lameness.
Reason and Alternative: See item 12.
Assuming that a UDP datagram of any length may be
sent. Criminally lame.
Reason: Various networks all have their limitations on maximum
transmission unit (MTU). As a result, fragmentation will occur,
and this increases the likelihood of a corrupted datagram (more
pieces to lose or corrupt). Also, the TCP/IP service providers
at the receiving end may not be capable of re-assembling a large,
fragmented datagram.
Alternative: Check for the maximum datagram size with the
SO_MAX_MSG_SIZE socket option, and don't send anything
larger. Better yet, be even more conservative. A max of 8K is
a good rule-of-thumb.
Assuming the UDP transmissions (especially multicast
transmissions) are reliable. Sinking in a morass of
lameness.
Reason: UDP has no reliability mechanisms (that's why we have
TCP).
Alternative: Use TCP and keep track of your own message
boundaries.
Applications that require vendor-specific extensions, and
cannot run (or worse yet, load) without them. Stooping to
unspeakable depths of lameness.
Reason: If you can't figure out the reason, it's time to hang
up your keyboard.
Alternative: Have a fallback position that uses only base
capabilities for when the extension functions are not
present.
Expecting errors when UDP datagrams are dropped by the sender,
receiver, or any router along the way. Seeping lameness from
every crack and crevice.
Reason: UDP is unreliable. TCP/IP stacks don't have to tell
you when they throw your datagrams away (a sender or receiver
may do this when they don't have buffer space available, and a
receiver will do it if they cannot reassemble a large fragmented
datagram.
Alternative: Expect to lose datagrams, and deal. Implement
reliability in your application protocol, if you need it (or
use TCP, if your application allows it).