PeerConnection cleanup

### Motivation
Cleanup `PeerConnection`; make code simpler and more maintainable.

### Changelog
* Align `send` methods in `PeerConnection` and `CryptoPeerConnection`; drop `send_raw`.
* Disentangle `PeerConnection` from `transport.py`; make classes more amenable to testing.
* Remove excessive `try` blocks. Do not silence really bad errors.
* Use global `zmq.Context` and let it manage itself.
I think it'd be great to have a consistent API (not sure if there's a way to enforce interfaces) so that you can just swap the Connection objects and it works just the same.

Now... we can also ask ourselves... do we want to allow for non-encrypted connections?

If the answer is no, then away with the class hierarchy, and we just make a PeerConnection class that works with encryption and has a method to send unencrypted data (or with this `encrypt`) flag when necessary (e.g. handshaking), or perhaps you leave the initial handshake to another class prior to establishing an encrypted connection between peers.