Introduced Channel abstraction as a layer on top of Connection.
There is a very important reason we need both of these concepts, it is *very* subtle, and it has everything to do with pipelining.
When we send a message to the server, we send it with a requestId.  Sometimes, we'll ask to receive a message.
When we ask to receive a message, we know we are looking for a message with a responseTo field equaling the requestId from
the message we sent.  This is all well and good until one of two things happen.  First, if the server starts replying to messages
out of order, we have to be prepared to read a message off the connection that belongs to someone else.  Second, if we allow
the possibility of sending multiple requests down a single connection without waiting for a reply from the first one,
then we must be prepared for the "race condition" where the sender of the second message is the first caller of receive.

In both the above cases, we *must* be able to call receiveMessage without validating that the responseTo field is what we expect.
How do we do that?  This is where the subtlety lies.  Other than some minor differences in the Channel and Connection interfaces
related to opening, the significant difference lies in the method signature of receiveMessage.

    Connection :  public ReplyMessage receiveMessage();
    Channel      :  public ReplyMessage receiveMessage(int requestId);

Given the above method signatures, it should be obvious that users of connections must be able to handle out of order replies
while users of channel do not need to.  Given this information, Channel is a higher-level construct than Connection

[Thanks to Craig Wilson for this explanation]