read in >1 byte chunks, and set a useful timeout on select()

this way, we're not rolling this loop over nearly as much, while still
preserving the overall timeout semantics
Changelog re #413, closes #454