How do I (gracefully) terminate a gSOAP server? - c++

I have gSOAP server generated from a WSDL file + a Qt GUI. The generated code works perfectly fine, except one point that causes my process to stay alive after GUI exits. (I'm deploying on Windows, so I have no signaling)
I need my GUI to stay alive (naturally) so I moved server-proxy object to a QObject-based class that the latter is moved to another QThread, and the I fire it up by an external signal. The server now runs on event-loop of its parent QObject and works fine.
The only problem is that I have no clue how to terminate server on exit. I tried tweaking generated code for server (is that really a good idea by the way?)
int MySweetService::run(int port)
{ if (!soap_valid_socket(this->soap->master) && !soap_valid_socket(this->bind(NULL, port, 100)))
return this->soap->error;
for (;;) // =====> Maybe here I can put my while(module_is_running_atomic_bool) ?
{ if (!soap_valid_socket(this->accept()))
{ if (this->soap->errnum == 0) // timeout?
this->soap->error = SOAP_OK;
break;
}
if (this->serve())
break;
this->destroy();
}
return this->soap->error;
}

Calling soap_done(&soap) from another thread terminates blocking call to accept() and next your "serving" thread. It works for me on Windows but I doesn't on Linux - it looks like gsoap has some multitasking issue. You also need some boolean flag to let "serving" thread know that you shut it down and it's not just error in gsope.

Related

Writing to QTcpSocket does not always emit readyRead signal on opposite QTcpSocket

I have been stuck on this for the past 5 days, I have no idea how to proceed.
Overview:
I have a client UI which interacts with a data handler library, and the data handler library utilizes a network manager library, which is where my problem lies.
More Info
Firstly, QT provides a basic example for interactions between a QTcpServer (Fortune Server)and a QTcpSocket (Fortune Client).
I thus implemented this code into an extremely basic example of my own, which works like a charm and has no issues.
My own adaption of fortune client and server for the record (basic)
Quick Explaination:
Server application runs, click on start server, then on the client side, enter text in field and click connect to server and text is displayed, easy!
Problem:
Implementing the code above into my network manager library, does not fire the QTcpSocket::readyRead() in the server application above.
It connects to the server, where the QTcpServer::newConnection() is fired, as expected, straight after which the client writes to the socket but the readyRead() on the server socket does not fire, however in the example given it does.
Note:
The same port and ip address is used in this server-client application example and my current application, and the server is also running.
Further Information:
From the above code, I copied over directly from the client. Only 2 things were changed/modified:
String that is sent to server
return types for method
This was copied into my network mannager ::write() method. When running my application, and instance of QMainWindow is passed via data handler class and creates an instance of my network manager class which inherits QObject and implements the Q_OBJECT macro.
Code Examples:
//client_UI Class (snippet):
data_mananger *dman = new data_mananger(this); //this -> QMainWindow
ReturnObject r = dman->NET_AuthenticateUser_GetToken(Query);
//data_manager library (snippet)
data_mananger::data_mananger(QObject *_parent) :
parent(_parent)
{}
ReturnObject data_mananger::NET_AuthenticateUser_GetToken(QString Query){
//Query like "AUTH;U=xyz#a;P=1234"
//convert query string to char
QByteArray ba = Query.toLatin1();
//send query and get QList return
ReturnCode rCode = networkManager.write(ba);
//...
}
//netman library (snippet)
//.h
class NETMANSHARED_EXPORT netman : public QObject
{
Q_OBJECT
public
netman();
netman(QObject *_parent);
//...
private:
QTcpSocket *tcp_con;
//...
};
//cpp
netman::netman(QObject *_parent) :
parent(_parent)
{
tcp_con = new QTcpSocket(parent);
}
return;
}
serverIP.setAddress(serverInfo.addresses().first().toIPv4Address());
}
ReturnCode netman::write(QByteArray message, int portNumber){
tcp_con->connectToHost(QHostAddress("127.0.0.1"), 5000);
if (!tcp_con->waitForConnected())
{
qDebug(log_lib_netman_err) << "Unable to connect to server";
return ReturnCode::FailedConnecting;
}
if (!tcp_con->isValid()) {
qDebug(log_lib_netman_err) << "tcp socket invalid";
return ReturnCode::SocketError;
}
if (!tcp_con->isOpen()) {
qDebug(log_lib_netman_err) << "tcp socket not open";
return ReturnCode::SocketError;
}
// QByteArray block(message);
QByteArray block;
QDataStream out(&block,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << QString("Hello world");
if (!tcp_con->write(block)){
qDebug(log_lib_netman_err) << "Unable to send data to server";
return ReturnCode::WriteFailed;
}
else{
qDebug(log_lib_netman_info) << "Data block sent";
return ReturnCode::SentSuccess;
}
}
Conclusion:
The core code of the client side has been fully implemented, yet I cannot see why this error occurs.
I would very much appreciate help/advice!
Add a tcp_con->flush() statement to the end of your write function.
Why/how this works
You weren't getting a readyRead signal in your receiver because the written data was being buffered into the socket but not actually transmitted 'over the wire'. The flush() command causes the buffer to be transmitted. From the docs
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.
How are you supposed to know
In my case a lot of experience/frustration with serial ports and flushing. It's the equivalent of "have you rebooted it?" in the socket debugging toolbox.
If everything else is working fine, you may not have to flush, but it's kind of application specific and depends on the lifetime of the socket, the TCP window size, socket option settings, and various other factors. That said, I always flush because I like having complete control over my sockets, and I want to make sure data is transmitted when I want it to be. I don't think it's a hack, but in some cases it could be indicative of some other problem. Again, application specific.
Why might the buffer not be flushing itself?
I'm pretty sure no flush is needed in the fortune server example because they disconnectFromHost at the end of the sendFortune() function, and from the Qt documentation:
Attempts to close the socket. If there is pending data waiting to be
written, QAbstractSocket will enter ClosingState and wait until all
data has been written.
The socket would disconnect if it were destroyed as well, but from what I can see of your code you aren't doing that either, and the buffer isn't full, so probably nothing is actually stimulating the buffer to flush itself.
Other causes can be:
flow control isn't returned to the event loop (blocking calls, etc), so the buffer flush is never performed.
Transmit is occuring inside of a loop, which seems like it will exit (e.g. while(dataToTransmit)), but in fact the condition never becomes false, which leads to the event loop being blocked.
Nagles algorithm: the buffer may be waiting for more data before it flushes itself to keep network throughput high. You can disable this by setting the QAbstractSocket::LowDelayOption, but it may adversely affect your throughput... it's normally used for latency-sensative applications.

Ensuring QSerialPort.close completes before program execution finishes?

I have an application using a QSerialPort, the very last thing my program does is call my closePort function which looks like this:
//Closes the port when we're done with it
void SerialCommSession::closePort()
{
connected = false;
if(m_port.isOpen())
{
qDebug() << "closing port";
m_port.close();
}
}
Some amount of the time it works fine, and the program starts up again without any issue. But maybe 75% of the time, when I try to run the program again it fails to open the port, returning error code 2. How long should QSerialPort.close() take to execute? And how can I ensure it completes?
Make your closePort function a slot and connect it to QCoreApplication::aboutToQuit:
connect(qApp, &QCoreApplication::aboutToQuit, someCommSessionPointer, &SerialCommSession::closePort)
When the event loop exits and before the application quits, your slot will be invoked. Make sure you have included <QCoreApplication> or one of its derived classes in order for the qApp macro to work.
Also:
How long should QSerialPort.close() take to execute? And how can I ensure it completes?
Slots in the same thread are invoked synchronously, so it can take as long as it needs to before control returns to the application. The application won't quit until your slot returns.

SDL_Flip deadlock

In my application, I've got a simple Execute method that runs the application,
greatly simplified, it looks something like this:
App::Execute()
{
Initialize();
SDL_Event event;
While(Running)
{
while(SDL_PollEvent(&event) > 0)
{
HandleEvent(&event);
}
Render();
}
}
and Render() looks like this, again simplified:
App::Render()
{
SDL_FillRect(MainSurface, NULL, 0x000000); // MainSurface is declared inside the class
// mCurrentScreen->Render(MainSurface);
SDL_Flip(MainSurface);
}
Now this works fine when the debugger is attached, but when I just run the executable,
the application freezes, attaching gdb to the process, I can tell the main thread is stuck inside SDL_Flip().
I have managed to narrow this issue down to me launching a separate thread that listens for clients using sockets, as commenting out the start of this second thread, makes it work in all modes!
The thread is started like this inside the class 'Server':
Server::Start()
{
mThread = std::thread([this](){ListenThread(this); });
}
ListenThread does the usual socket setup
getaddrinfo(), socket(), bind(), listen() and finally, the call to the blocking function
accept().
Again, this server, and the rendering and everything, works perfectly when a debugger is attached, it is only when launched "directly" that it hangs (forcing me to KILL the process to stop it).
The Server and App has no common data structures, Server doesn't know anything about App,
the only relation between them two is that App sets a callback function on Server.
mServer->Callback = [this](Command cmd){ this->CommandReceived(); });
Edit(Append): This only occurs on my PC, I have the exact same setup (ubuntu, gcc, sdl version etc the same) on my laptop, and it works fine on my laptop.
Note sure if/how it's relevant, but I'm running Ubuntu inside a Hyper-V virtual machine on Windows 8, if that's any help.

Indy10 TCP Server Freezing

I using Indy with C++ Builder XE3. It's perfect system but i have some problems. IdTCPServer works really good but when i have some connections on him and i want to stop server then my application freezed. I try to tell how i do it step by step:
1) Starting application (and server listening)
2) wait for new connections (or simulate it, no difference)
3) when we have 10-15 connections - then try to stop server listening.
4) when code came to IdTCPServer1->Active = false - application will be frozen
i made little video. Maybe it explain situation much better. http://www.youtube.com/watch?v=BNgTxYbLx8g
And here my code:
OnConnect:
EnterCriticalSection(&CritLock);
++ActiveConnections;
SetActiveConnections(ActiveConnections);
LeaveCriticalSection(&CritLock);
OnDisconnect:
EnterCriticalSection(&CritLock);
--ActiveConnections;
SetActiveConnections(ActiveConnections);
LeaveCriticalSection(&CritLock);
StopServer Code:
void TForm1::StopServer()
{
TList *list = IdTCPServer1->Contexts->LockList();
try
{
for(int i = 0; i < list->Count; ++i)
{
TIdContext *AContext = reinterpret_cast<TIdContext*>(list->Items[i]);
try
{
if (AContext->Connection->Connected())
{
AContext->Connection->IOHandler->InputBuffer->Clear();
AContext->Connection->IOHandler->WriteBufferCancel();
AContext->Connection->IOHandler->WriteBufferClear();
AContext->Connection->IOHandler->WriteBufferClose();
AContext->Connection->IOHandler->CloseGracefully();
AContext->Connection->Disconnect();
}
}
catch (const Exception &e)
{
}
}
}
__finally
{
IdTCPServer1->Contexts->UnlockList();
}
IdTCPServer1->Contexts->Clear();
//IdTCPServer1->StopListening();
IdTCPServer1->Active = false;
}
Thanks for advise!
You need to get rid of all your StopServer() code except for the very last line. When TIdTCPServer is deactivated, it performs all necessary cleanups for you. DO NOT DO IT YOURSELF (especially since you are doing it wrong anyway).
void TForm1::StopServer()
{
IdTCPServer1->Active = false;
}
Now, with just that code, if your app is still freezing, then that means you are deadlocking the main thread. That happens if you call StopServer() in the context of the main thread and one of two things are happening in your server code:
one of your TIdTCPServer event handlers performs a synchronized operation to the main thread (either via TIdSync or TThread::Synchronize()).
one of your TIdTCPServer event handlers swallows Indy exceptions and does not allow TIdTCPServer to terminate one or more client threads correctly when needed.
Internally, the TIdTCPServer::Active property setter closes all active sockets and waits for their respective threads to fully terminate, blocking the calling thread until the property setter exits. If yoou are deactivating the server in the main thread and one of the server threads performs a sync that the main thread cannot process, or otherwise does not terminate correctly when it should be, that will block the server deactivation from exiting and thus deadlock the main thread.
So make sure that:
you are not performing sync operations to the main thread while the server is being deactivated by the main thread. If you must sync, then deactivate the server in a worker thread instead so the main thread is not blocked anymore.
your event handlers are not swallowing any Indy EIdException-derived exceptions in try/catch blocks. If you catch such an exception, re-throw it when you are finshed using it. Let TIdTCPServer handle any Indy exceptions so it can perform internal cleanups as needed.
Lastly, on a side note, you do not need to keep track of connections manually. TIdTCPServer already does that for you in the Contexts property. If you need to know how many clients are currently connected at any moment, simply Lock() the Contexts list, read its Count property (or do anything else you need to do with the clients), and then Unlock() the list.

assign signal handler by pid

If I know a pid of a certain process that doesn't run the code(say firefox)
how do I assign a signal handler(say SIGINT) to it?
I have now :
pid = fork();
printf("forked and my pid is %d\n",pid);
//check for errors
if (pid<0){
printf("Error: invoking fork to start ss has failed, Exiting\n ");
exit(1);
}
//the child process runs the gulp
if (pid==0){
printf("STARTING THE FIREFOX\n");
//calling signal(somehandler,SIGINT); here will bind the child, which is replaced by the firefox new process,hence won't invoke the "somehandler"
if (execv(args[0],args)<0){
perror("Error: running s with execvp has failed, Exiting\n");
}
//invoking signal(somehandler,SIGINT); will obviously not do anything
printf("IVE BEEN KILLED\n");
}
//dad is here
printf("DAD IS GOING TO KILL\n");
if (pid>0){
sleep(6);
//how do I bind a handler to that signal????
kill(get_pidof(string("firefox")),SIGINT);
}
You can only establish a signal handler from within the process. Put another way, you can't make firefox call your signal handler when it gets a SIGINT.
EDIT
As you noticed, indeed signal handlers are not kept after an exec - the image of the process is replaced so it wouldn't make sense. So, like I said before: you can't make firefox call your handler even if you control its parent.
I need my program to run another program(say firefox), and to know
when the firefox died or crashed
In that case you want to establish a signal handler for SIGCHLD: your process will jump to it when the child dies.
As Cnucitar answered here, you can only change signal handler from inside the process.
If you wanted to make a signal handler inside firefox, you could patch it, perhaps thru a plugin. But I am sure that would be a bad idea.