Reputation: 33
I have implemented a websocket server in a QCoreApplication. As soon as a connection is established with a client, I wish to send a series of messages to it immediately in real-time with a delay of 0.5
seconds between messages. However, the individual messages reach the client only after all the messages have been sent or right after sendMyMessages()
method returns in the below implementation of my websocket server. If I have a huge number of messages to send, the client has to wait for a long time before getting all the messages in one go.
MyWebSocketServer.h
#ifndef MYWEBSOCKETSERVER_H
#define MYWEBSOCKETSERVER_H
#include <QtCore/QObject>
#include <QtCore/QList>
#include <QtCore/QByteArray>
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
QT_FORWARD_DECLARE_CLASS(QWebSocket)
class MyWebSocketServer : public QObject
{
Q_OBJECT
public:
explicit MyWebSocketServer(quint16 port,
bool debug = false,
QObject *parent = nullptr);
~MyWebSocketServer();
Q_SIGNALS:
void closed();
private Q_SLOTS:
void onNewConnection();
void socketDisconnected();
private:
void sendMyMessages(QWebSocket *client);
QWebSocketServer *m_pWebSocketServer;
QList<QWebSocket *> m_clients;
bool m_debug;
};
#endif // MYWEBSOCKETSERVER_H
MyWebSocketServer.cpp
#include "MyWebSocketServer.h"
#include <iostream>
#include <fstream>
#include <chrono>
#include <thread>
#include <QtWebSockets/qwebsocketserver.h>
#include <QtWebSockets/qwebsocket.h>
#include <QtCore/QDebug>
QT_USE_NAMESPACE
MyWebSocketServer::MyWebSocketServer(quint16 port,
bool debug,
QObject *parent)
: QObject(parent)
, m_pWebSocketServer(new QWebSocketServer(
QStringLiteral("My WebSocket Server"),
QWebSocketServer::NonSecureMode,
this))
, m_debug(debug)
{
connect(m_pWebSocketServer,
&QWebSocketServer::newConnection,
this,
&MyWebSocketServer::onNewConnection);
connect(m_pWebSocketServer,
&QWebSocketServer::closed,
this,
&MyWebSocketServer::closed);
m_pWebSocketServer->listen(QHostAddress::LocalHost, port);
}
MyWebSocketServer::~MyWebSocketServer()
{
m_pWebSocketServer->close();
qDeleteAll(m_clients.begin(), m_clients.end());
}
void MyWebSocketServer::onNewConnection()
{
QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection();
connect(pSocket,
&QWebSocket::disconnected,
this,
&MyWebSocketServer::socketDisconnected);
m_clients << pSocket;
sendMyMessages(pSocket);
}
void MyWebSocketServer::sendMyMessages(QWebSocket *client)
{
std::fstream jsonStringFileHandler;
jsonStringFileHandler.open("my-messages.txt", std::ios::in);
if (jsonStringFileHandler.is_open())
{
std::string message;
while(getline(jsonStringFileHandler, message))
{
// Individual messages don't go through immediately
// Only after this method returns, all the messages show up on the client's end
// Is it possible to send the individual messages immediately? (probably with a 0.5 second delay)
client->sendTextMessage(QString::fromUtf8(message.c_str()));
}
jsonStringFileHandler.close();
}
}
void MyWebSocketServer::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
if (pClient)
{
m_clients.removeAll(pClient);
pClient->deleteLater();
}
}
Only after the sendMyMessages()
returns, the client gets all the messages. It is not in real-time. I understand it would be possible to achieve what I am after using some asynchronous programming technique but I unable to figure out a way to set it up in my implementation of the websocket server.
Here is an implementation of the websocket server that worked for me in Python using the websockets
and asyncio
modules. However, I wish to implement the same logic in C++ using Qt.
import asyncio
import websockets
async def sendMyMessages(websocket, path):
with open("my-messages.txt") as fp:
lines = fp.readlines()
for line in lines:
await asyncio.sleep(0.5)
await websocket.send(line.strip())
start_server = websockets.serve(sendMyMessages, "127.0.0.1", 3000)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
Upvotes: 0
Views: 1136
Reputation: 33
One of the approaches I found uses QWebSocket::flush()
immediately after a call to QWebSocket::sendTextMessage()
.
From the docs on QWebSocket::flush()
,
This function writes as much as possible from the internal write buffer to the underlying network socket, without blocking. If any data was written, this function returns true; otherwise false is returned. Call this function if you need QWebSocket to start sending buffered data immediately. The number of bytes successfully written depends on the operating system. In most cases, you do not need to call this function, because QWebSocket will start sending data automatically once control goes back to the event loop.
However, I am not sure if this is the right approach as the doc indicates that the data may not be written to the underlying network socket reliably. I will be happy to know if there is a better technique out there!
client->sendTextMessage(QString::fromUtf8(message.c_str()));
client->flush();
std::this_thread::sleep_for(std::chrono::milliseconds(50));
Upvotes: 0