--- /dev/null
+#include <cstring>
+#include "communicator.h"
+
+namespace {
+
+using namespace Msp::Net;
+
+struct Handshake
+{
+ unsigned hash;
+};
+
+
+class HandshakeProtocol: public Protocol
+{
+public:
+ HandshakeProtocol();
+};
+
+HandshakeProtocol::HandshakeProtocol():
+ Protocol(0x7F00)
+{
+ add<Handshake>()(&Handshake::hash);
+}
+
+
+class HandshakeReceiver: public PacketReceiver<Handshake>
+{
+private:
+ unsigned hash;
+
+public:
+ HandshakeReceiver();
+ unsigned get_hash() const { return hash; }
+ virtual void receive(const Handshake &);
+};
+
+HandshakeReceiver::HandshakeReceiver():
+ hash(0)
+{ }
+
+void HandshakeReceiver::receive(const Handshake &shake)
+{
+ hash = shake.hash;
+}
+
+}
+
+
+namespace Msp {
+namespace Net {
+
+Communicator::Communicator(StreamSocket &s, const Protocol &p, ReceiverBase &r):
+ socket(s),
+ protocol(p),
+ receiver(r),
+ handshake_status(0),
+ buf_size(1024),
+ in_buf(new char[buf_size]),
+ in_begin(in_buf),
+ in_end(in_buf),
+ out_buf(new char[buf_size]),
+ good(true)
+{
+ socket.signal_data_available.connect(sigc::mem_fun(this, &Communicator::data_available));
+}
+
+Communicator::~Communicator()
+{
+ delete[] in_buf;
+ delete[] out_buf;
+}
+
+void Communicator::initiate_handshake()
+{
+ if(handshake_status!=0)
+ throw sequence_error("handshaking already done");
+
+ send_handshake();
+ handshake_status = 1;
+}
+
+void Communicator::data_available()
+{
+ if(!good)
+ return;
+
+ in_end += socket.read(in_end, in_buf+buf_size-in_end);
+ try
+ {
+ bool more = true;
+ while(more)
+ {
+ if(handshake_status==2)
+ {
+ more = receive_packet(protocol, receiver);
+ }
+ else
+ {
+ HandshakeProtocol hsproto;
+ HandshakeReceiver hsrecv;
+ if((more = receive_packet(hsproto, hsrecv)))
+ {
+ if(hsrecv.get_hash()==protocol.get_hash())
+ {
+ if(handshake_status==0)
+ send_handshake();
+ handshake_status = 2;
+ signal_handshake_done.emit();
+ }
+ else
+ good = false;
+ }
+ }
+ }
+ }
+ catch(...)
+ {
+ good = false;
+ throw;
+ }
+}
+
+bool Communicator::receive_packet(const Protocol &proto, ReceiverBase &recv)
+{
+ int psz = proto.get_packet_size(in_begin, in_end-in_begin);
+ if(psz && psz<=in_end-in_begin)
+ {
+ char *pkt = in_begin;
+ in_begin += psz;
+ proto.disassemble(recv, pkt, psz);
+ return true;
+ }
+ else
+ {
+ if(in_end==in_buf+buf_size)
+ {
+ unsigned used = in_end-in_begin;
+ memmove(in_buf, in_begin, used);
+ in_begin = in_buf;
+ in_end = in_begin+used;
+ }
+ return false;
+ }
+}
+
+void Communicator::send_handshake()
+{
+ Handshake shake;
+ shake.hash = protocol.get_hash();
+
+ HandshakeProtocol hsproto;
+ unsigned size = hsproto.assemble(shake, out_buf, buf_size);
+ socket.write(out_buf, size);
+}
+
+} // namespace Net
+} // namespace Msp