Unable to push data to server using Asynchronous RPC with DCE pipes - asynchronous

I want to push data to the server using Asynchronous RPC with pipes. Here is my code:
//file: Xasyncpipe.idl:
interface IMyAsyncPipe
{
//define the pipe type
typedef pipe int ASYNC_INTPIPE;
int MyAsyncInputPipe(
handle_t hBinding,
[in] ASYNC_INTPIPE *inpipe) ;
};
//file:Xasyncpipe.acf:
interface IMyAsyncPipe
{
[async] MyAsyncInputPipe () ;
} ;
//file:Client.cpp
mian()
{
// Creates a binding handle.
...
RPC_ASYNC_STATE Async;
status = RpcAsyncInitializeHandle(&Async, sizeof(RPC_ASYNC_STATE));
Async.UserInfo = NULL;
Async.NotificationType = RpcNotificationTypeIoc;
Async.u.IOC.hIOPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
ASYNC_INTPIPE inputPipe;
// Calls the RPC function.
MyAsyncInputPipe(&Async, hBinding, &inputPipe);
}
//file:Server.cpp
void MyAsyncInputPipe(PRPC_ASYNC_STATE state, handle_t hBinding, ASYNC_INTPIPE *pipe)
{
std::cout << "Input Test" << std::endl;
}
I added a breakpoint in function MyAsyncInputPipe, and the breakpoint is never triggered.
I change the Xasyncpipe.idl from [in] ASYNC_INTPIPE *inpipe to [out] ASYNC_INTPIPE *inpipe, break point is triggered.
Does anyone know the reason?

Server will not receive data until client side pipe pushes a terminal signal.
//push terminal signal
inputPipe.push(inputPipe.state, NULL, 0);

Related

nghttp2: Using server-sent events to be use by EventSource

I'm using nghttp2 to implement a REST server which should use HTTP/2 and server-sent events (to be consumed by an EventSource in the browser). However, based on the examples it is unclear to me how to implement SSE. Using res.push() as in asio-sv.cc doesn't seem to be the right approach.
What would be the right way to do it? I'd prefer to use nghttp2's C++ API, but the C API would do as well.
Yup, I did something like that back in 2018. The documentation was rather sparse :).
First of all, ignore response::push because that's the HTTP2 push -- something for proactively sending unsolicited objects to the client before it requests them. I know it sounds like what you need, but it is not -- the typical use case would be proactively sending a CSS file and some images along with the originally requested HTML page.
The key thing is that your end() callback must eventually return NGHTTP2_ERR_DEFERRED whenever you run out of data to send. When your application somehow obtains more data to be sent, call http::response::resume().
Here's a simple code. Build it as g++ -std=c++17 -Wall -O3 -ggdb clock.cpp -lssl -lcrypto -pthread -lnghttp2_asio -lspdlog -lfmt. Be careful, modern browsers don't do HTTP/2 over a plaintext socket, so you'll need to reverse-proxy it via something like nghttpx -f '*,8080;no-tls' -b '::1,10080;;proto=h2'.
#include <boost/asio/io_service.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/signals2.hpp>
#include <chrono>
#include <list>
#include <nghttp2/asio_http2_server.h>
#define SPDLOG_FMT_EXTERNAL
#include <spdlog/spdlog.h>
#include <thread>
using namespace nghttp2::asio_http2;
using namespace std::literals;
using Signal = boost::signals2::signal<void(const std::string& message)>;
class Client {
const server::response& res;
enum State {
HasEvents,
WaitingForEvents,
};
std::atomic<State> state;
std::list<std::string> queue;
mutable std::mutex mtx;
boost::signals2::scoped_connection subscription;
size_t send_chunk(uint8_t* destination, std::size_t len, uint32_t* data_flags [[maybe_unused]])
{
std::size_t written{0};
std::lock_guard lock{mtx};
if (state != HasEvents) throw std::logic_error{std::to_string(__LINE__)};
while (!queue.empty()) {
auto num = std::min(queue.front().size(), len - written);
std::copy_n(queue.front().begin(), num, destination + written);
written += num;
if (num < queue.front().size()) {
queue.front() = queue.front().substr(num);
spdlog::debug("{} send_chunk: partial write", (void*)this);
return written;
}
queue.pop_front();
spdlog::debug("{} send_chunk: sent one event", (void*)this);
}
state = WaitingForEvents;
return written;
}
public:
Client(const server::request& req, const server::response& res, Signal& signal)
: res{res}
, state{WaitingForEvents}
, subscription{signal.connect([this](const auto& msg) {
enqueue(msg);
})}
{
spdlog::warn("{}: {} {} {}", (void*)this, boost::lexical_cast<std::string>(req.remote_endpoint()), req.method(), req.uri().raw_path);
res.write_head(200, {{"content-type", {"text/event-stream", false}}});
}
void onClose(const uint32_t ec)
{
spdlog::error("{} onClose", (void*)this);
subscription.disconnect();
}
ssize_t process(uint8_t* destination, std::size_t len, uint32_t* data_flags)
{
spdlog::trace("{} process", (void*)this);
switch (state) {
case HasEvents:
return send_chunk(destination, len, data_flags);
case WaitingForEvents:
return NGHTTP2_ERR_DEFERRED;
}
__builtin_unreachable();
}
void enqueue(const std::string& what)
{
{
std::lock_guard lock{mtx};
queue.push_back("data: " + what + "\n\n");
}
state = HasEvents;
res.resume();
}
};
int main(int argc [[maybe_unused]], char** argv [[maybe_unused]])
{
spdlog::set_level(spdlog::level::trace);
Signal sig;
std::thread timer{[&sig]() {
for (int i = 0; /* forever */; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds{666});
spdlog::info("tick: {}", i);
sig("ping #" + std::to_string(i));
}
}};
server::http2 server;
server.num_threads(4);
server.handle("/events", [&sig](const server::request& req, const server::response& res) {
auto client = std::make_shared<Client>(req, res, sig);
res.on_close([client](const auto ec) {
client->onClose(ec);
});
res.end([client](uint8_t* destination, std::size_t len, uint32_t* data_flags) {
return client->process(destination, len, data_flags);
});
});
server.handle("/", [](const auto& req, const auto& resp) {
spdlog::warn("{} {} {}", boost::lexical_cast<std::string>(req.remote_endpoint()), req.method(), req.uri().raw_path);
resp.write_head(200, {{"content-type", {"text/html", false}}});
resp.end(R"(<html><head><title>nghttp2 event stream</title></head>
<body><h1>events</h1><ul id="x"></ul>
<script type="text/javascript">
const ev = new EventSource("/events");
ev.onmessage = function(event) {
const li = document.createElement("li");
li.textContent = event.data;
document.getElementById("x").appendChild(li);
};
</script>
</body>
</html>)");
});
boost::system::error_code ec;
if (server.listen_and_serve(ec, "::", "10080")) {
return 1;
}
return 0;
}
I have a feeling that my queue handling is probably too complex. When testing via curl, I never seem to run out of buffer space. In other words, even if the client is not reading any data from the socket, the library keep invoking send_chunk, asking for up to 16kB of data at a time for me. Strange. I have no idea how it works when pushing more data more heavily.
My "real code" used to have a third state, Closed, but I think that blocking events via on_close is enough here. However, I think you never want to enter send_chunk if the client has already disconnected, but before the destructor gets called.

How process signals in an external thread

A external component has a callback which executes in its internal thread start with std::thread, I want to create a qt component(not UI) in this thread, connect other qt components signal to this qt component, and let the slot function execute in this internal thread.
I expect to execute to run the event loop once callback triggerd to process pending slot functions invoking.
// call in a thread start with std::thread in ExternalComponent, this method invoke periodically.
void ExternalComponent::InternalProcessing() {
//do other thing...
//invoke callback
callback();
}
void CustomQtComponent::Init() {
externalComponent.SetCallback([]() {
// first time, create a Worker
if (worker_ == nullptr) {
worker_ = new Worker();
}
// process pending signals(invoke worker_ slot methods) in this thread
// ...
// do other things.
});
}
// call by ui thread
void CustomQtComponent::DoSomething() {
// do xxxx
// ...
// emit a signal, to let something process in callback threads
// emit cutstomSignalWhichConnectToWokerSlots();
}
Because external threads not start by QThread, so though we can get the QThread object in its thread(in callback), but it has no event loop.
Could I construct a QEventLoop in callback thread, and let it receive and process signals sending to worker_?
Working example with trivial classes:
...{
std::thread([this]()
{
qDebug() << "current thread is" << QThread::currentThread();
auto recv = new Worker;
connect(this, &MainWin::call, recv, &Worker::callMe);
/* Note that recv's slots will be called in the thread where
the recv object lives */
QEventLoop eventLoop;
// Do not forget to setup exit mechanism
connect(qApp, &QApplication::aboutToQuit,
&eventLoop, &QEventLoop::quit);
// Do not forget to start the loop but only after connections
eventLoop.exec();
}).detach();
// After start the thread we can trigger the signal
QThread::msleep(1000); // make sure won't be called earlier than connect()
// Check the threads are different
qDebug() << "current thread is" << QThread::currentThread();
emit call();
}
Where
void Worker::callMe()
{
qDebug() << "callMe invoked";
qDebug() << "current thread is" << QThread::currentThread();
}
See also the list of useful links about objects, signals-slots and threading in Qt, in the answer: https://stackoverflow.com/a/60755238/4149835

pthread is not starting for class instance

NOTE: C++98
Hi, I'm a little new to c++ and I am writing a databaes program and am attempting to start a timer using the boost::asio package using pthread. The aim of the timer is to start after sql queries have been placed inside a buffer, of which will run an execute function if nothing has been received for a period of time. I have managed to get it to compile, but it doesn't look like the pthread instance is starting.
I have called the pthread inside my getInstance method, and the boost::asio alarm has been set up accordingly. What I will show below is that by calling io_run() directly starts the timer falls into a loop within the alarm.
database.h
void *run_io(void *arg);
class Database
{
private:
static Database *dbInstance; //= NULL;
public:
boost::asio::io_service io_service;
boost::posix_time::millisec interval;
boost::asio::deadline_timer timer;
pthread_t timerThread;
public:
static Database &getInstance()
{
if (!dbInstance)
{
dbInstance = new Database();
// pthread_create(&dbInstance->timerThread,NULL,run_io,&dbInstance->io_service);
std::cout << " INSTANCE CREATED " << std::endl;
pthread_create(&dbInstance->timerThread, NULL, run_io, (void *)&dbInstance->io_service);
// pthread_join(&dbInstance->timerThread, NULL);
}
return *dbInstance;
}
};
database.cpp
Database *Database::dbInstance = NULL;
Database::Database()
: interval(2000), timer(io_service, interval) {}
Database::~Database()
{
sqlite3_close(db);
}
void Database::setAlarm(const boost::system::error_code& /* e */)
{
std::cout << "[TEST] WE ARE IN SET ALARM " << std::endl;
DB_WRITE_TIME = 500;
boost::posix_time::milliseconds interval(DB_WRITE_TIME);
// Reschedule the timer for 1 second in the future:
timer.expires_at(timer.expires_at() + interval);
// Posts the timer event
timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
}
int Database::buffer()
{
// DO BUFFER STUFF
timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
// io_service.run() <-- uncommenting this results in the loop
return rc ;
}
void *run_io(void *arg)
{
boost::asio::io_service *io_service = (boost::asio::io_service *)arg;
io_service->run();
}
So I don't feel like the pthread is even starting. I tried putting a print statement in there to see if it came out, and nothing appeared in my terminal.
---- EDIT ----
I have made changes as per Sehe's advice, however it still does not look like I am able to call the alarm handler (setAlarm()). I had to slightly modify it to be compatible with the whole program, but essentially it is this (I gave the interval time a value of 5000 to give it enough time for the tests):
database.h
class Database
{
private:
static boost::shared_ptr<Database> dbInstance;
private:
typedef boost::asio::io_service io_service;
io_service io;
boost::scoped_ptr<io_service::work> work;
boost::posix_time::millisec interval;
boost::asio::deadline_timer timer;
boost::thread timerThread;
void run_io()
{
std::cout << "ENTER IO THREAD" << std::endl;
io.run();
std::cout << "LEAVE IO THREAD" << std::endl;
}
public:
static Database &getInstance()
{
if (!dbInstance)
{
std::cout << " INSTANCE CREATED " << std::endl;
dbInstance.reset(new Database());
dbInstance->timerThread = boost::thread(boost::bind(&Database::run_io,dbInstance));
}
return *dbInstance;
}
Database(); // <-- default constructor (doesn't take any args)
~Database();
database.cpp
boost::shared_ptr<Database> Database::dbInstance;
static const int DB_WRITE_TIME = 5000;
Database::Database()
: work(new io_service::work(io)), interval(5000), timer(io, interval)
{
// std::cout << " CONSTRUCTED " << std::endl;
}
Database::~Database()
{
// std::cout << " DESTROYED " << std::endl;
// sqlite3_close(db);
}
void Database::setAlarm(const boost::system::error_code& ec)
{
std::cout << "[TEST] WE ARE IN SET ALARM - ec message = " << ec.message() << std::endl;
executeSqlInBuffer(); // once timer expire, call the execute function
if(!ec)
{
boost::posix_time::milliseconds interval(DB_WRITE_TIME);
timer.expires_from_now(interval);
timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
}
}
void Database::teardown()
{
// std::cout << " INSTANCE SHUTTING DOWN " << std::endl;
timer.cancel(); // stop timer loop
work.reset(); // allows io.run() to exit
if(timerThread.joinable())
{
std::cout << " JOINED " << std::endl;
timerThread.join(); // releasing bound of shared_ptr
}
else std::cout << " NOT JOINED " << std::endl;
dbInstance.reset(); // releasing instance
}
int Database::buffer()
{
// do buffering
if(buffer.size() == max_size)
{
executeSqlInBuffer();
}
std::cout << timer.expires_from_now(interval) << std::endl;
// std::cout << " ~ BEFORE TIMER ~ " << std::endl;
timer.async_wait(boost::bind(&Database::setAlarm, this, _1));
return 1;
}
main.cpp
int main()
{
pthread_t thread1; // a few pthreads in main that handle other areas of the program.
pthread_create(&thread1,NULL,thread1Arg,NULL);
pthread_t dbThread; // my pthread for the database
pthread_create(&dbThread,NULL,dbThreadArg,NULL);
Database& database = Database::getInstance();
database.teardown();
pthread_join(thread1,NULL);
pthread_join(dbThread,NULL);
return 0;
}
You can also see here that it enters and leaves the IO thread, and creates an instance, plus the debug output for timer.expires_from_now(interval):
INSTANCE CREATED
JOINED
ENTER IO THREAD
LEAVE IO THREAD
...
...
0 ---> first cycle
1 ---> second cycle
...
1 ---> nth cycle
I'm very ccnfused why anyone who uses Boost or C++11 (or both...) would ever use raw pthread threads (see e.g. C++ boost asynchronous timer to run in parallel with program for a good juxtaposition).
The real problem is likely that you have io_service running out of work (see e.g. https://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/reference/io_service__work.html).
If you have no pending async operations the thread just exits.
Another problem is accuracy issues with
timer.expires_at(timer.expires_at() + interval);
It's possible that some handlers take so much time that by the time you schedule your next alarm, the deadline has already expired. It's probably better to use
timer.expires_from_now(interval);
Note this also matches the comment better. The comment suffers from comment already because it says "1 second" but it is actually some defined constant DB_WRITE_TIME
or separate your timer from the other handlers in some other way to guarantee accurate scheduling.
Finally, you had UB due to the absense of any shutdown. The static instance never gets destroyed, but what's worth the non-detached thread never is joined, creating undefined behaviour at shutdown.
This problem is actually almost identical to the one recently discussed here, where I also explains the way work guards work in more detail: asio::io_service is ending immediately with work
Here's a c++11 rewrite with the necessary fix:
Since I now noticed you're that person stuck in c++03 land for some weird reason, a Boost Thread version:
C++03 DEMO/Boost Thread
Live On Coliru
#include <boost/asio.hpp>
#include <boost/make_shared.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/thread.hpp>
#include <iostream>
static const int DB_WRITE_TIME = 500;
class Database
{
private:
static boost::shared_ptr<Database> dbInstance;
Database()
: work(new io_service::work(io)),
interval(750),
timer(io, interval)
{
std::cout << "INSTANCE CREATED" << std::endl;
}
void on_timer_completed(const boost::system::error_code& ec) {
std::cout << "[on_timer_completed] " << ec.message() << std::endl;
if (!ec) {
boost::posix_time::milliseconds interval(DB_WRITE_TIME);
// Reschedule the timer
timer.expires_from_now(interval);
timer.async_wait(boost::bind(&Database::on_timer_completed, this, _1));
}
}
int buffer()
{
// DO BUFFER STUFF
timer.expires_from_now(interval);
timer.async_wait(boost::bind(&Database::on_timer_completed, this, _1));
// io_service.run() <-- uncommenting this results in the loop
return 1; // rc ;
}
public:
void do_stuff() {
buffer(); // whatever it does
}
void teardown() {
std::cout << "INSTANCE SHUTTING DOWN\n";
timer.cancel(); // stop timer loop
work.reset(); // allows io.run() to exit
if (timerThread.joinable()) {
timerThread.join(); // releasing the bound shared_ptr
}
dbInstance.reset(); // releasing the instance
}
~Database() {
//sqlite3_close(db);
std::cout << "INSTANCE DESTROYED\n";
}
private:
typedef boost::asio::io_service io_service;
io_service io;
boost::scoped_ptr<io_service::work> work;
boost::posix_time::millisec interval;
boost::asio::deadline_timer timer;
boost::thread timerThread;
void run_io() {
std::cout << "ENTER IO THREAD" << std::endl;
io.run();
std::cout << "LEAVE IO THREAD" << std::endl;
}
public:
static Database &getInstance()
{
if (!dbInstance)
{
dbInstance.reset(new Database());
dbInstance->timerThread =
boost::thread(boost::bind(&Database::run_io, dbInstance));
}
return *dbInstance;
}
};
boost::shared_ptr<Database> Database::dbInstance;
int main() {
Database& db = Database::getInstance();
boost::this_thread::sleep_for(boost::chrono::seconds(1));
db.do_stuff();
boost::this_thread::sleep_for(boost::chrono::seconds(3));
// ....
db.teardown();
}
Prints
INSTANCE CREATED
ENTER IO THREAD
[on_timer_completed] Success
[on_timer_completed] Success
[on_timer_completed] Success
[on_timer_completed] Success
[on_timer_completed] Success
INSTANCE SHUTTING DOWN
[on_timer_completed] Operation canceled
LEAVE IO THREAD
INSTANCE DESTROYED

read and write to qtcpsocket using qdatastream

There are various methods of reading and writing from a QTcpSocket using a QDatastream as seen here
The difference is, I will be sending more than "one packet" or blocks.
A basic implementation on the server (sending) side and client (recieving) is seen below - only the actual sending and receiving snippets are shown
More Info, What I tried:
When writing to a QTcpSocket, I attempted to use the QTcpSocket::canReadLine() however this fails straightup after the QTcpSocket::waitForReadReady() signal fires.
I then tried QDataStream::atEnd() in a while loop which causes a system crash :(
The code below shows my latest attempt of going through the QDataStream docs, and utilzing the commitTransaction where it states
If no full packet is received, this code restores the stream to the initial position, after which you need to wait for more data to arrive.
Under the heading Using Read Transactions. But ofcourse, this just reads one block that is sent, i.e the first block.
Question:
When writing to a QTcpSocket multiple times, and flushing the socket each time to send that data, how can I read this from a QTcpSocket as it is send, keep the original "send structure"?
The example below only reads the first block and ends. I would like to read the block containing "Response 2" and "Response 3".
Code Implementations:
//server.h
//...
QTcpSocket *clientSocket = nullptr;
QDataStream in;
//...
//server.cpp
//...
in.setDevice(clientSocket);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QString nextFortune;
in >> nextFortune;
if (in.commitTransaction())
ui->lblOut->setText(nextFortune);
if (clientSocket != nullptr) {
if (!clientSocket->isValid()) {
qDebug() << "tcp socket invalid";
return;
}
if (!clientSocket->isOpen()) {
qDebug() << "tcp socket not open";
return;
}
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << QString(QString("Response:") + nextFortune);
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 2");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 3 here, and this is the end!");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
clientSocket->disconnectFromHost();
}
//...
And the client side
//client.h
//...
QTcpSocket *tcp_con = nullptr;
QDataStream in;
//...
//client.cpp
//...
if(!tcp_con->waitForReadyRead()){
qDebug(log_lib_netman_err) << "tcp con timeout for reading";
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::SocketError, QString());
}
in.setDevice(tcp_con);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QList<QString> data_rcv = QList<QString>();
QString s;
// while (tcp_con->canReadLine()) {
// in >> s;
// data_rcv.push_back(s);
// }
// while (!in.read) {
in >> s;
data_rcv.push_back(s);
// }
while (!in.commitTransaction()){
qDebug(log_lib_netman_info) << "waiting for more data";
in >> s;
data_rcv.push_back(s);
// qDebug(log_lib_netman_err) << "Unable to send data to server";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::FailedReceiving, QString());
}
// if (s.isEmpty()) {
// qDebug(log_lib_netman_err) << "Empty response recieved";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::NoDataRecieved, QString());
// }
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::ReceivedSuccess, data_rcv);
Help would be greatly appreciated!

QTcpServer -- how to stop client threads

My gui have a two button's, one to start a server & other to stop a server.
Brief :---
Once server is started, on every new client request i will create a new thread & this will handle communication with the client.
Detail :--
When start button is pressed, I am creating an object 'tcpserverobjectWrapper' which creates an another object 'tcpserverobject' this object creats an Qtcpserver.
Now this Qtcpserver is listing for new connection. And when a new connection comes i create an 'TcpSocketThreadWrapperObject' object which
creates a thread & this thread handles communication with client . Also 'tcpserverobject' keeps the list of new client request objects created
'QList<TcpSocketThreadWrapperObject *> TcpSocketThreadWrapperObjectList;' .
I am able to connect to server from telnet clients & it creates new thread for each client & works fine.
When stop button pressed i am able to stop server & client threads.
But i have two problems here :---
1> Everytime client send some data to server. I get this kind of QsocketNotifier. What is this ?
QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread
2> If i press stop button on GUI i am able to stop the threads succesfully.
But how to stop the threads & delete the objects created for every client when client send 'STOP command' to server or closes the connection with server ?
I will also have to delete the following objects created on each client request ?
client request --> TcpSocketThreadWrapperObject -- creates --> TcpSocketThreadObject -- creates --> TcpSocketThreadObject
Can someone suggest how to solve above two problems ? Reply on this will be appreciated.
Here is the code :---
================= start & stop buttons handler =====
void MainWindow::on_actionStop_triggered()
{
if(b_threadAlreadyStarted)
{
/* ------------------ Tcp server object ------------------------*/
b_threadAlreadyStarted = false;
delete p_tcpserverobjectWrapper;
/* ------------------ Tcp server object ------------------------*/
}
}
void MainWindow::on_actionStart_triggered()
{
if(!b_threadAlreadyStarted)
{
/* ------------------ Tcp server object ------------------------*/
b_threadAlreadyStarted =true;
p_tcpserverobjectWrapper = new tcpserverobjectWrapper(this,modelCANalyzer);
qDebug() << " \n start ";
/* ------------------ Tcp server object ------------------------*/
}
}
======== tcpserverobjectWrapper class ===============
// Main server object wrapper
class tcpserverobjectWrapper : public QObject
{
Q_OBJECT
public:
explicit tcpserverobjectWrapper(QMainWindow *ptrWidget, QStandardItemModel *modelCANalyzer, QObject *parent=0);
~tcpserverobjectWrapper();
//Device thread object
tcpserverobject *m_tcpserverobject;
};
tcpserverobjectWrapper::tcpserverobjectWrapper(QMainWindow *ptrWidget , QStandardItemModel *modelCANalyzer,QObject *parent) :
QObject(parent)
{
m_tcpserverobject = new tcpserverobject ;
//save model
m_tcpserverobject->modeltable = modelCANalyzer;
m_tcpserverobject->ptrmainwindow = ptrWidget;
qDebug() << "\n tcp server thread started";
}
tcpserverobjectWrapper::~tcpserverobjectWrapper()
{
qDebug() << " \n called delete later on tcpserverobjectWrapper .. !!";
m_tcpserverobject->deleteLater(); // ---------------------> change it to - delete m_tcpserverobject
qDebug() << " \n tcp server object successfully quited .. !! ";
}
========== tcpserverobject object ==================
class tcpserverobject : public QObject
{
Q_OBJECT
public:
explicit tcpserverobject(QObject *parent = 0);
~tcpserverobject();
/*!
Pointer to QStandardItemModel to be used inside - canTableView
*/
QStandardItemModel *modeltable;
//mainwindow pointer
QMainWindow *ptrmainwindow;
// Create list of new -- socket thread wrapper objects
QList<TcpSocketThreadWrapperObject *> TcpSocketThreadWrapperObjectList;
private:
QTcpServer *tcpServer;
signals:
public slots:
void on_newConnection();
};
tcpserverobject::tcpserverobject(QObject *parent) :
QObject(parent), tcpServer(0)
{
tcpServer = new QTcpServer;
// Connect slot of the server
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(on_newConnection()));
//lisen on socket
if (!tcpServer->listen(QHostAddress::LocalHost, SERVER_PORT )) {
qDebug() << "\n returning from server listning error .. !!! ";
return;
}
qDebug() << "\n server listning";
}
tcpserverobject::~tcpserverobject()
{
// to do
while (!TcpSocketThreadWrapperObjectList.isEmpty())
delete TcpSocketThreadWrapperObjectList.takeFirst();
}
void tcpserverobject::on_newConnection()
{
QByteArray block;
block.append(" \n Hello from server .. !!!") ;
QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()),
clientConnection, SLOT(deleteLater()));
// Create new thread for this .. client request ..!!
qDebug() << "\n New connection request ..!!!";
qDebug() << "\n New client from:" << clientConnection->peerAddress().toString();
clientConnection->write(block);
clientConnection->flush();
// create new tcp object
TcpSocketThreadWrapperObject* TcpSocketThreadWrapperObjectPtr = new TcpSocketThreadWrapperObject(clientConnection);
// Append object to the list
TcpSocketThreadWrapperObjectList.append(TcpSocketThreadWrapperObjectPtr);
}
============ TcpSocketThreadWrapperObject ==============
// Main device thread object
class TcpSocketThreadWrapperObject : public QObject
{
Q_OBJECT
public:
explicit TcpSocketThreadWrapperObject(QTcpSocket *m_pTcpSocket , QObject *parent = 0);
~TcpSocketThreadWrapperObject();
/*!
pointer for write thread
*/
QThread m_TcpSocketRWThread;
/// pointer to the socketthread object
class TcpSocketThreadObject *m_pTcpSocketThreadObject;
signals:
public slots:
};
// constructor for the deviceThreadObject
TcpSocketThreadWrapperObject::TcpSocketThreadWrapperObject(QTcpSocket *m_pTcpSocket , QObject *parent) :
QObject(parent)
{
m_pTcpSocketThreadObject = new TcpSocketThreadObject(m_pTcpSocket);
//set flag for event loop -- make while(1)
m_pTcpSocketThreadObject->m_bQuit = false;
// connect the signal & slot
connect(&m_TcpSocketRWThread,SIGNAL(started()),m_pTcpSocketThreadObject,SLOT(dowork_socket()));
// Move thread to object
m_pTcpSocketThreadObject->moveToThread(&m_TcpSocketRWThread);
//Start the thread
m_TcpSocketRWThread.start();
}
TcpSocketThreadWrapperObject::~TcpSocketThreadWrapperObject()
{
//set flag for event loop -- make while(0)
m_pTcpSocketThreadObject->m_bQuit = false;
// Wait for the thread to terminate
m_TcpSocketRWThread.quit();
m_TcpSocketRWThread.wait();
// Delete the object
m_pTcpSocketThreadObject->deleteLater();
qDebug() << "\n deleted - TcpSocketThreadWrapperObject";
}
======== TcpSocketThreadObject object ========
class TcpSocketThreadObject : public QObject
{
Q_OBJECT
public:
explicit TcpSocketThreadObject(QTcpSocket *m_pTcpSocketTemp , QObject *parent = 0);
~TcpSocketThreadObject();
/*!
Pointer to TCP socket -- created by the server
*/
QTcpSocket *m_pclientConnectionSocket;
/*!
Termination control main thread
*/
volatile bool m_bQuit;
signals:
public slots:
void dowork_socket();
};
// constructor for the deviceThreadObject
TcpSocketThreadObject::TcpSocketThreadObject(QTcpSocket *m_pTcpSocketTemp , QObject *parent) :
QObject(parent)
{
m_pclientConnectionSocket = m_pTcpSocketTemp;
// todo
}
TcpSocketThreadObject::~TcpSocketThreadObject()
{
// todo
}
void TcpSocketThreadObject::dowork_socket()
{
QByteArray block;
block.append(" \n hi again .. !!!") ;
// Write to socket
m_pclientConnectionSocket->write(block);
m_pclientConnectionSocket->flush();
// Close socket
m_pclientConnectionSocket->disconnectFromHost();
qDebug() << "\n entring loop of socket thread ..!!!";
while(!m_bQuit)
{
// while loop --> send/rx command from client
}
}
======= output of one client connected to server =====
New connection request ..!!!
New client from: "127.0.0.1"
QSocketNotifier: socket notifiers cannot be enabled from another thread
QSocketNotifier: socket notifiers cannot be disabled from another thread
entring loop of socket thread ..!!!
I think the simplest solution to all of this is to stop using QThread.
QTcpSocket and QTcpServer are both asynchronous, so when you receive a connection, you only need to create a class to wrap the QTcpSocket and this class handles the reading of the data, having connected the QTcpSocket signals to the class's slots.
If you're getting a lot of connections, then you'll be creating lots of threads. More threads than processor cores is just a waste of time.
If each connection is requesting a lot of work to be done by the server, you can then create separate worker objects and move those to a different thread, but overall, I recommend that you do not use separate threads for QTcpServer and QTcpSockets.

Resources