}
QObject::connect(rsock, SIGNAL(disconnected(RibbonSocket*)),
this, SLOT(onDisconnection(RibbonSocket*)));
+ QObject::connect(rsock, SIGNAL(messageReceived(RibbonSocket*,QByteArray)),
+ this, SLOT(onMessageReceived(RibbonSocket*,QByteArray)));
}
void RibbonServer::onDisconnection(RibbonSocket* r)
{
sockets.removeOne(r);
qDebug() << "Socket disconnected:" << r->s();
+ r->deleteLater();
if (!server->isListening()) {
server->listen(_addr, _port);
qDebug() << "Server listening on" << _addr << ":" << _port;
}
}
+
+void RibbonServer::onMessageReceived(RibbonSocket* r, QByteArray buf)
+{
+ qDebug() << "server: got data from" << r->s()->peerAddress() << r->s()->peerPort();
+ qDebug() << " message was:" << buf.constData();
+ // TODO: do something with the packet in buf.constData()
+}
#include "ribbonsocket.h"
+#include <QDebug>
-RibbonSocket::RibbonSocket(QTcpSocket* sock, QObject* parent) : QObject(parent), socket(sock)
+RibbonSocket::RibbonSocket(QTcpSocket* sock, QObject* parent) :
+ QObject(parent),
+ socket(sock),
+ state(WANT_LENGTH),
+ bytes_wanted(0)
{
- QObject::connect(socket, SIGNAL(connected()),
- this, SLOT(onConnect()));
QObject::connect(socket, SIGNAL(disconnected()),
this, SLOT(onDisconnect()));
QObject::connect(socket, SIGNAL(readyRead()),
return socket;
}
-void RibbonSocket::onConnect()
+void RibbonSocket::onDisconnect()
{
- emit connected(this);
+ emit disconnected(this);
}
-void RibbonSocket::onDisconnect()
+/**
+ * Returns true if progress was made, false if no progress was made and we need
+ * more bytes from the socket
+ */
+bool RibbonSocket::tryProcess()
{
- emit disconnected(this);
+ switch(state) {
+ case WANT_LENGTH:
+ qDebug() << "Want length, buf size before read:" << buf.length();
+ if (readUntilBufHas(4)) {
+ // Slice 4 octets off as big-endian integer
+ QByteArray size_buf = buf.left(4);
+ buf = buf.mid(4);
+ QDataStream stream(size_buf);
+ stream >> bytes_wanted;
+ // Optimization: preallocate the right number of bytes
+ // TODO: decide on a maximum acceptable packet length, to avoid
+ // arbitrarily large packet buffer
+ buf.reserve(bytes_wanted);
+ qDebug() << "length:" << bytes_wanted;
+ state = WANT_DATA;
+ return true;
+ }
+ break;
+ case WANT_DATA:
+ qDebug() << "Want data, buf size before read:" << buf.length();
+ if (readUntilBufHas(bytes_wanted)) {
+ QByteArray data = buf.left(bytes_wanted);
+ buf = buf.mid(bytes_wanted);
+ // Memory optimization: release the rest of the buffer
+ buf.squeeze();
+ qDebug() << "data:" << buf;
+ emit messageReceived(this, data);
+ state = WANT_LENGTH;
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+/**
+ * returns true if buf has enough bytes
+ * false otherwise
+ */
+bool RibbonSocket::readUntilBufHas(int desired_buffer_length)
+{
+ int size_to_read = desired_buffer_length - buf.length();
+ QByteArray tmp = socket->read(size_to_read);
+ qDebug() << "read" << tmp.length() << "from socket";
+ buf += tmp;
+ return buf.length() >= desired_buffer_length;
}
void RibbonSocket::onReadyRead()
{
- emit readyRead(this);
+ while (tryProcess()) {}
}
#ifndef RIBBONSOCKET_H
#define RIBBONSOCKET_H
+#include <QtCore/QByteArray>
#include <QtNetwork/QTcpSocket>
class RibbonSocket : public QObject {
Q_OBJECT
+ enum RibbonSocketState {
+ WANT_LENGTH = 0,
+ WANT_DATA = 1,
+ };
+
public:
RibbonSocket(QTcpSocket* socket, QObject* parent=0);
// Consider s to be final. Don't modify it.
const QTcpSocket* s();
signals:
- void connected(RibbonSocket* s);
void disconnected(RibbonSocket* s);
- void readyRead(RibbonSocket* s);
+ void messageReceived(RibbonSocket* s, QByteArray buf);
private:
virtual ~RibbonSocket();
QTcpSocket* socket;
+ QByteArray buf;
+ RibbonSocketState state;
+ quint32 bytes_wanted;
+ bool tryProcess();
+ bool readUntilBufHas(int desired_buffer_length);
private slots:
- void onConnect();
void onDisconnect();
void onReadyRead();
};