use persistent channels

There were several previous commits that attempted to do this. The
problem wasn't with the pooling, (though the "pool" implemented here is
much simpler and easier to reason about), but it was in the message
recipient handler. If there was a race and another message was sent
before the first was able to be processed, both built up in the byte
buffer. But we only read the first in the buffer, so the second was
lost. The fix is to loop until the entire buffer is read in the handler
and parse all the messages.