jbr May 2, 2010
TCP is a network protocol that delivers a reliable stream of bytes between two peers. The underlying packets within which the actual quantized buffers of bytes arrive are supposed to be abstracted away. This is true when you call fread and fwrite on the stream or when you use Tcl blocking io with a handle obtained with socket. Unfortunately when the underlying OS read is called it is free the return the "available" data (open to interpretation?). The recv socket call can be used to wait for and return the next logical chunk sent from the peer.
The behavior of recv is a pleasant mix of blocking and non-blocking io which is just the right thing in many situations. This is particularly true when a client server protocol is used with variable length data packets and the protocol itself does not specify the number of bytes in each command and reply packet. You can argue that this is poor design, but not that it doesn't work. Emulating this behavior with non-blocking io and retries seems tedious to me.
I recently implemented one of these protocols in TCL and used this critcl snippet to smooth over the cracks.
package require critcl critcl::ccode { #include <tcl.h> } critcl::cproc recv { Tcl_Interp* interp char* sock } ok { char bytes[1500]; int mode; int fd; int n; Tcl_GetChannelHandle(Tcl_GetChannel(interp, sock, &mode), TCL_READABLE, &fd); if ( (n = recv(fd, bytes, 1500, 0)) < 0 ) { return TCL_ERROR; } Tcl_SetByteArrayObj(Tcl_GetObjResult(interp), (void *) bytes, n); return TCL_OK; }