Created
February 17, 2017 17:04
-
-
Save micjabbour/3fdd561bd129a25e33725d6618e6de21 to your computer and use it in GitHub Desktop.
Tcp Socket with heartbeats
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <QtWidgets> | |
#include <QtNetwork> | |
class SyncTcpSocket : public QTcpSocket{ | |
Q_OBJECT | |
public: | |
explicit SyncTcpSocket(QObject* parent= nullptr):QTcpSocket(parent){ | |
setSocketOption(QAbstractSocket::LowDelayOption, 1); | |
heartbeatTimer.setInterval(100); | |
connect(&heartbeatTimer, &QTimer::timeout, | |
this, &SyncTcpSocket::SendHeartBeat); | |
timeoutTimer.setInterval(500); | |
connect(&timeoutTimer, &QTimer::timeout, | |
this, &SyncTcpSocket::HeartbeatTimeout); | |
connect(this, &QTcpSocket::readyRead, | |
this, &SyncTcpSocket::Receive); | |
connect(this, &QAbstractSocket::stateChanged, | |
this, &SyncTcpSocket::StateChanged); | |
StateChanged(); | |
} | |
~SyncTcpSocket(){} | |
Q_SLOT void SendHeartBeat(){ | |
QDataStream ds(this); | |
ds << (qint16)0; //heartbeat value | |
flush(); | |
lastSendMs= lastSendTime.elapsed(); | |
lastSendTime.start(); | |
if(lastSendMs < minSendMs) minSendMs= lastSendMs; | |
if(lastSendMs > maxSendMs) maxSendMs= lastSendMs; | |
sumSendMs+= lastSendMs; | |
countSendMs++; | |
emit SentHeartbeat(); | |
} | |
Q_SLOT void HeartbeatTimeout(){ | |
qDebug() << "heartbeat timeout, aborting. . ."; | |
abort(); | |
close(); | |
} | |
Q_SLOT void Receive(){ | |
if(bytesAvailable() < 2) return; | |
QDataStream ds(this); | |
qint16 val; | |
ds >> val; | |
timeoutTimer.start(); | |
lastReceiveMs= lastReceiveTime.elapsed(); | |
lastReceiveTime.start(); | |
if(lastReceiveMs < minReceiveMs) minReceiveMs= lastReceiveMs; | |
if(lastReceiveMs > maxReceiveMs) maxReceiveMs= lastReceiveMs; | |
sumReceiveMs+= lastReceiveMs; | |
countReceiveMs++; | |
emit ReceivedHeartbeat(); | |
} | |
Q_SLOT void StateChanged(){ | |
if(state() == QAbstractSocket::ConnectedState){ | |
heartbeatTimer.start(); | |
timeoutTimer.start(); | |
lastReceiveTime.start(); | |
lastSendTime.start(); | |
} else { | |
heartbeatTimer.stop(); | |
timeoutTimer.stop(); | |
} | |
} | |
//connection properties getters | |
int GetLastReceiveMs(){ return lastReceiveMs; } | |
int GetLastSendMs(){ return lastSendMs; } | |
double GetAvgReceiveMs(){ return sumReceiveMs/countReceiveMs; } | |
double GetAvgSendMs(){ return sumSendMs/countSendMs; } | |
int GetMaxReceiveMs(){ return maxReceiveMs; } | |
int GetMaxSendMs(){ return maxSendMs; } | |
int GetMinReceiveMs(){ return minReceiveMs; } | |
int GetMinSendMs(){ return minSendMs; } | |
signals: | |
void SentHeartbeat(); | |
void ReceivedHeartbeat(); | |
private: | |
QTimer heartbeatTimer{this}; | |
QTimer timeoutTimer{this}; | |
QTime lastReceiveTime; | |
QTime lastSendTime; | |
int lastReceiveMs{0}, lastSendMs{0}; | |
double sumReceiveMs{0}, sumSendMs{0}; | |
int countReceiveMs{0}, countSendMs{0}; | |
int maxReceiveMs{0}, maxSendMs{0}; | |
int minReceiveMs{INT_MAX}, minSendMs{INT_MAX}; | |
}; | |
class SyncTcpServer : public QTcpServer{ | |
public: | |
static const quint16 defaultListenPort = 9955; | |
explicit SyncTcpServer(QObject* parent= nullptr):QTcpServer(parent){} | |
~SyncTcpServer(){} | |
SyncTcpSocket* nextPendingConnection(){ | |
SyncTcpSocket* pendingConn= | |
dynamic_cast<SyncTcpSocket*>(QTcpServer::nextPendingConnection()); | |
if(!pendingConn) return NULL; | |
return pendingConn; | |
} | |
bool Listen(){ | |
return listen(QHostAddress::Any, defaultListenPort); | |
} | |
protected: | |
void incomingConnection(qintptr socketDescriptor){ | |
SyncTcpSocket* newSocket= new SyncTcpSocket(this); | |
newSocket->setSocketDescriptor(socketDescriptor); | |
addPendingConnection(newSocket); | |
} | |
}; | |
class SyncSocketWatcherWidget : public QWidget{ | |
Q_OBJECT | |
public: | |
explicit SyncSocketWatcherWidget(QWidget* parent= nullptr):QWidget(parent){ | |
layout.addWidget(&sendLabel, 0, 0, Qt::AlignCenter); | |
layout.addWidget(&sendLast, 1, 0); | |
layout.addWidget(&sendMax, 2, 0); | |
layout.addWidget(&sendAvg, 3, 0); | |
layout.addWidget(&sendMin, 4, 0); | |
layout.addWidget(&receiveLabel, 0, 1, Qt::AlignCenter); | |
layout.addWidget(&receiveLast, 1, 1); | |
layout.addWidget(&receiveMax, 2, 1); | |
layout.addWidget(&receiveAvg, 3, 1); | |
layout.addWidget(&receiveMin, 4, 1); | |
layout.addWidget(&socketState, 5, 0, 1, 2, Qt::AlignCenter); | |
} | |
~SyncSocketWatcherWidget(){} | |
void SetWatchedSocket(SyncTcpSocket* watchedSocket){ | |
if(this->watchedSocket) | |
disconnect(this->watchedSocket, 0, this, 0); | |
this->watchedSocket = watchedSocket; | |
if(watchedSocket){ | |
connect(watchedSocket, &SyncTcpSocket::ReceivedHeartbeat, | |
this, &SyncSocketWatcherWidget::UpdateValues); | |
connect(watchedSocket, &SyncTcpSocket::SentHeartbeat, | |
this, &SyncSocketWatcherWidget::UpdateValues); | |
socketState.setText("Connected"); | |
} else { | |
socketState.setText("Disconnected"); | |
} | |
} | |
Q_SLOT void UpdateValues(){ | |
sendLast.setText(QString("Interval Last: %0 ms").arg(watchedSocket->GetLastSendMs())); | |
sendMax.setText(QString("Interval Max: %0 ms").arg(watchedSocket->GetMaxSendMs())); | |
sendAvg.setText(QString("Interval Avg: %0 ms").arg(watchedSocket->GetAvgSendMs())); | |
sendMin.setText(QString("Interval Min: %0 ms").arg(watchedSocket->GetMinSendMs())); | |
receiveLast.setText(QString("Interval Last: %0 ms").arg(watchedSocket->GetLastReceiveMs())); | |
receiveMax.setText(QString("Interval Max: %0 ms").arg(watchedSocket->GetMaxReceiveMs())); | |
receiveAvg.setText(QString("Interval Avg: %0 ms").arg(watchedSocket->GetAvgReceiveMs())); | |
receiveMin.setText(QString("Interval Min: %0 ms").arg(watchedSocket->GetMinReceiveMs())); | |
} | |
private: | |
QGridLayout layout{this}; | |
SyncTcpSocket* watchedSocket{nullptr}; | |
QLabel receiveLabel{"Receive Intervals"}; | |
QLabel receiveLast{"Interval Last: "}; | |
QLabel receiveMax{"Interval Max: "}; | |
QLabel receiveAvg{"Interval Avg: "}; | |
QLabel receiveMin{"Interval Min: "}; | |
QLabel sendLabel{"Send Intervals"}; | |
QLabel sendLast{"Interval Last: "}; | |
QLabel sendMax{"Interval Max: "}; | |
QLabel sendAvg{"Interval Avg: "}; | |
QLabel sendMin{"Interval Min: "}; | |
QLabel socketState{"Disconnected"}; | |
}; | |
class ServerWindow : public QWidget{ | |
Q_OBJECT | |
public: | |
explicit ServerWindow(QWidget* parent= nullptr):QWidget(parent){ | |
layout.addWidget(&buttonStart); | |
layout.addWidget(&watcherWidget); | |
connect(&buttonStart, &QPushButton::clicked, | |
this, &ServerWindow::StartServer); | |
connect(&server, &SyncTcpServer::newConnection, | |
this, &ServerWindow::AcceptTheOnlyConnection); | |
} | |
~ServerWindow(){} | |
Q_SLOT void AcceptTheOnlyConnection(){ | |
socket= server.nextPendingConnection(); | |
watcherWidget.SetWatchedSocket(socket); | |
connect(socket, &QTcpSocket::disconnected, | |
this, &ServerWindow::ShowError); | |
connect(socket, static_cast<void(QTcpSocket::*)(QTcpSocket::SocketError)>(&QTcpSocket::error), | |
this, &ServerWindow::ShowError); | |
connect(socket, &QTcpSocket::disconnected, | |
this, [=](){qDebug() << "server socket disconnected";}); | |
connect(socket, static_cast<void(QTcpSocket::*)(QTcpSocket::SocketError)>(&QTcpSocket::error), | |
this, [=](QAbstractSocket::SocketError e){qDebug() << "server socket error " << e;}); | |
connect(socket, &QObject::destroyed, this, []{qDebug() << "server socket destroyed.";}); | |
server.close(); | |
} | |
Q_SLOT void StartServer(){ | |
if(!server.Listen()){ | |
QMessageBox::critical(this, "Error Listening", | |
"Could Not Start Server", QMessageBox::Ok); | |
return; | |
} | |
watcherWidget.SetWatchedSocket(nullptr); | |
if(socket){ | |
socket->abort(); | |
socket->deleteLater(); | |
socket= nullptr; | |
} | |
buttonStart.setEnabled(false); | |
} | |
Q_SLOT void ShowError(){ | |
if(socket){ | |
socket->deleteLater(); | |
socket= nullptr; | |
} | |
watcherWidget.SetWatchedSocket(nullptr); | |
buttonStart.setEnabled(true); | |
} | |
private: | |
QVBoxLayout layout{this}; | |
QPushButton buttonStart{"Start Server"}; | |
SyncSocketWatcherWidget watcherWidget; | |
SyncTcpServer server; | |
SyncTcpSocket* socket{nullptr}; | |
}; | |
class ClientWindow : public QWidget{ | |
Q_OBJECT | |
public: | |
explicit ClientWindow(QWidget* parent= nullptr):QWidget(parent){ | |
layout.addLayout(&hLayout); | |
hLayout.addWidget(&lineEditAddress); | |
hLayout.addWidget(&buttonConnect); | |
layout.addWidget(&watcherWidget); | |
lineEditAddress.setPlaceholderText("Server Address"); | |
connect(&buttonConnect, &QPushButton::clicked, this, &ClientWindow::ConnectToServer); | |
} | |
~ClientWindow(){ | |
delete socket; | |
} | |
Q_SLOT void ConnectToServer(){ | |
socket = new SyncTcpSocket(this); | |
socket->connectToHost(lineEditAddress.text(), SyncTcpServer::defaultListenPort); | |
watcherWidget.SetWatchedSocket(socket); | |
connect(socket, &SyncTcpSocket::disconnected, this, &ClientWindow::DisconnectedFromServer); | |
connect(socket, static_cast<void(SyncTcpSocket::*)(SyncTcpSocket::SocketError)>(&SyncTcpSocket::error), | |
this, &ClientWindow::DisconnectedFromServer); | |
connect(socket, &SyncTcpSocket::disconnected, | |
this, [=](){qDebug() << "client socket disconnected";}); | |
connect(socket, static_cast<void(SyncTcpSocket::*)(SyncTcpSocket::SocketError)>(&SyncTcpSocket::error), | |
this, [=](QAbstractSocket::SocketError e){qDebug() << "client socket error " << e;}); | |
connect(socket, &QObject::destroyed, this, []{qDebug() << "client socket destroyed.";}); | |
lineEditAddress.setEnabled(false); | |
buttonConnect.setEnabled(false); | |
} | |
Q_SLOT void DisconnectedFromServer(){ | |
if(socket){ | |
socket->deleteLater(); | |
socket= nullptr; | |
} | |
lineEditAddress.setEnabled(true); | |
buttonConnect.setEnabled(true); | |
watcherWidget.SetWatchedSocket(nullptr); | |
} | |
private: | |
QVBoxLayout layout{this}; | |
QHBoxLayout hLayout; | |
QLineEdit lineEditAddress{"127.0.0.1"}; | |
QPushButton buttonConnect{"Connect To Server"}; | |
SyncSocketWatcherWidget watcherWidget; | |
SyncTcpSocket* socket{nullptr}; | |
}; | |
int main(int argc, char* argv[]){ | |
QApplication a(argc, argv); | |
ServerWindow serverW; | |
serverW.show(); | |
ClientWindow clientW; | |
clientW.show(); | |
return a.exec(); | |
} | |
#include "main.moc" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment