I have to send data reliable from a client using QTcpSocket to a server using QTcpServer.
The connection is established once and the data is send line by line with terminator of "\r\n" (The server is splitting the incoming data on this terminator)
If need to know which line could be successful and which line not.
This works as expected. But if I unplug the server network cable from the network, the client is still writing data and the "bytesWritten" signal is still emitted.
But no write error occurred and the "error" signal is not emitted. It seems QTcpSocket is writing the data to an internal buffer even if the TPC connection is lost. Using flush() has no effect.
Example code based on the fortune client:
Client::Client(QWidget *parent)
: QDialog(parent), networkSession(0)
{
m_messageCount=0;
QString ipAddress= "192.168.1.16";
m_socket = new QTcpSocket(this);
//No effect...
//m_socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
connect(m_socket, SIGNAL(connected()), this, SLOT(Connected()));
connect(m_socket, SIGNAL(disconnected()), this, SLOT(DisConnected()));
connect(m_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(OnBytesWritten(qint64)));
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));
}
void Client::connectToHost()
{
m_socket->connectToHost("192.168.1.16", 1234);
}
void Client::Connected()
{
qDebug() << "Connected()";
QTimer::singleShot(1000, this, SLOT(SendNextRecord()));
}
void Client::DisConnected()
{
qDebug() << "DisConnected()";
}
void Client::SendNextRecord()
{
m_messageCount++;
QByteArray singleRecord=QString("Nr: %1 Some Text").arg(m_messageCount).toUtf8();
singleRecord.append("\r\n");
Q_ASSERT(m_socket->isValid());
qDebug() << "Sending: " <<singleRecord;
//bytesSend always > 0
qint64 bytesSend=m_socket->write(singleRecord);
//No effect
m_socket->flush();
qDebug() <<"bytes Send:" << bytesSend;
}
//Signal is still emitted even if network cable is unplugged
void Client::OnBytesWritten(qint64 bytes)
{
qDebug() << "OnBytesWritten:" << bytes;
//No effect
m_socket->flush();
QTimer::singleShot(1000, this, SLOT(SendNextRecord()));
}
//Signal not emitted even if network cable is unplugged
void Client::displayError(QAbstractSocket::SocketError socketError)
{
qDebug() << "Socket error";
}
Can I change this behaviour ?
Related
I'm working on a Qt5 project. I have a button that issues a clicked() signal and its slot has the following code which writes to the serial port:
void Dialog::on_startStream_clicked()
{
QByteArray ba;
if(esp->isOpen()){
esp->write("1");
if(esp->canReadLine()){
ba = esp->readLine();
ba.replace("\xFE", "");
ba = ba.simplified();
QString ba_with_time = stamp->currentDateTime().toString("MM.dd.yyyy hh:mm:ss ");
ba_with_time.append(ba);
ui->output->appendPlainText(ba_with_time);
qDebug() << ba_with_time;
}
}
}
After establishing the connection to the serial port, the first time I click on the button nothing happens. Subsequent clicks work correctly.
I'm using PlatformIO to upload the Arduino code to the ESP32 and in the PlatformIO serial monitor there is output immediately after I issue the command on the first time, which makes me think the problem is my Qt code.
How can I fix it so that Qt can read the buffer on the first button click?
QByteArry ba; // member of Dialog class
// connect the QSerialPort::readyRead() signal after creation of esp
connect(esp, &QSerialPort::readyRead, this, &Dialog::onDataReady);
...
void Dialog::on_startStream_clicked()
{
if(esp->isOpen()){
esp->write("1");
}
}
// The "onDataReady()" is called every time you receive data via serial port
void Dialog::onDataReady()
{
do{
ba += esp->readAll(); // buffer received data
int i = ba.indexOf("\n"); // i assume your message is \n terminated
if(i != -1){
QByteArray ba1 = ba.mid(0, i);
// modify the data
qDebug() << ba1;
ba.remove(0, i); // remove message from receive buffer
}
} while(esp->bytesAvailable());
}
My setup is as follows: Arduino Mega UART3 <--> Voltage Level shifter <--> Rpi4
All the code can be found here
Data is being sent synchronously from the Arduino to the Rpi at 100ms and 1000ms (depending on the importance of the data). And also data is being sent from the Rpi back to the Arduino asynchronously on GUI input (There is also a sync sent at 1000ms in case some frames are lost/corrupted).
Coms are handled in a separate thread than the GUI to provide the best user experience.
Well, all is working fine but I have been trying unsuccessfully to detect if the Rpi has lost connection with the Arduino. (In the other direction is covered with a serial watchdog.)
I wanted to avoid the use of another watchdog in the Qt part but I cannot find a suitable signal to detect the lost serial connection as the QSerialPort keeps reporting a Timeout error randomly... and there are no other methods that detect successfully the disconnection.
Here below is the workaround that I am trying to use without much success: (Probably it needs a major rewrite... but I need to be pointed in the right direction first)
// Serial Port Initialization
m_Serial = new QSerialPort();
m_Serial->setPortName("ttyS0");
//m_Serial->setPortName("ttyUSB0");
m_Serial->setBaudRate(QSerialPort::Baud115200);
m_Serial->setDataBits(QSerialPort::Data8);
m_Serial->setParity(QSerialPort::NoParity);
m_Serial->setStopBits(QSerialPort::OneStop);
m_Serial->setFlowControl(QSerialPort::NoFlowControl);
m_Serial->open(QIODevice::ReadWrite);
qDebug() << "SerialPort Status: " << m_Serial->isOpen();
emit serialConnected(m_Serial->isReadable());
while(!abort)
{
QThread::msleep(5);
while (!isConected(m_Serial)){
emit serialConnected(false);
isSerialConected = false;
QThread::sleep(1);
qDebug() << "Serial Error: ";
//qDebug() << "Is readable?: " << m_Serial->isReadable();
//qDebug() << "Bytes availiable?: " << m_Serial->bytesAvailable();
mutex.lock();
abort = _abort;
mutex.unlock();
}
if (!isSerialConected){
emit serialConnected(true);
isSerialConected=true;
}
mutex.lock();
abort = _abort;
mutex.unlock();
if(!m_outFrameQueue->isEmpty())
{
//qDebug() << "Frame empty";
{ *sendData*}
} else
{
if (m_Serial->waitForReadyRead(10) )
{ *readData*}
And the function that I am trying to use to detect if is actually connected:
bool SerialWorker::isConected(QSerialPort *m_Serial){
// qDebug() << "SerialPort Error: " << m_Serial->error();
// qDebug() << "SerialPort isReadable: " << m_Serial->isReadable();
// qDebug() << "SerialPort isReadable: " << m_Serial->bytesAvailable();
if (m_Serial->error() == QSerialPort::SerialPortError::NoError){
return true;
}
else if (m_Serial->error() == QSerialPort::SerialPortError::TimeoutError){
m_Serial->clearError();
//m_Serial->reset();
return true;
}else{
m_Serial->reset();
m_Serial->close();
m_Serial->clearError();
QThread::sleep(1);
m_Serial->open(QIODevice::ReadWrite);
return false;
}
}
I want to connet with Qt on windows to my PIC32 UDP server. With a test program in C, I can connect and get the answer, but with Qt it is impossible. What am I doing wrong ?
As you can see I use an ACTIVE WAITING with the while, and my slot doesn't seems to be triggered. Maybe you can see my mistake here ? I hate active waiting....
Here is my code on Qt :
void MuTweezer::run()
{
QHostAddress sender;
quint16 senderPort;
QByteArray datagram;
qint64 pendingBytes;
int cpt = 0;
// Message to send
QByteArray message("Hello that's charly");
// m_imuRcvSocket.bind(10000); why is it for in a client side !?
// SEEMS to NOT BE TRIGGERED
connect(&m_imuRcvSocket, SIGNAL(readyRead()), this, SLOT(recu()));
while(m_isRunning && cpt++ < 10)
{
// send the initial message
qint64 bytesSent= m_imuRcvSocket.writeDatagram(message, QHostAddress("192.168.0.15"), 10000);
cout << "Bytes sent : " << bytesSent << endl;
// We wait the answer from the server
while(!m_imuRcvSocket.hasPendingDatagrams());
// If there is no datagram available, this function returns -1
if((pendingBytes = m_imuRcvSocket.pendingDatagramSize()) == -1)
continue;
datagram.resize(pendingBytes);
m_imuRcvSocket.readDatagram(datagram.data(), datagram.size(),
&sender, &senderPort);
cout << "================="
<< "\nMessage from <" << sender.toString().toStdString().substr(7) << "> on port " << senderPort
<< "\nString : " << datagram.data()
<< "\nSize: " << pendingBytes << " Bytes (characters)\n"
<< "=================" <<
endl;
}
}
Here is my code on the PIC32, as you can see, once I receive a message, I send the answer, it allows me to make a bidirectionnal communication :
if(!UDPIsOpened(mySocket)){
DBPRINTF("Socket CLOSED");
continue; // go back to loop beginning
}
DBPRINTF("Socket OPEN");
if(!(lengthToGet = UDPIsGetReady(mySocket)))
continue;
// get the string
// UDPGetArray : returns the number of bytes successfully read from the UDP buffer.
if((lengthWeGot = UDPGetArray(message, lengthToGet)))
UDPDiscard(); // Discards any remaining RX data from a UDP socket.
/* Modifie it and send it back */
if(UDPIsPutReady(mySocket)){
message[20]= 'Z';
message[21]= 'i';
message[22]= 'b';
message[23]= 'o';
UDPPutArray(message, lengthWeGot);
UDPFlush();
}
Any idea ?
Try to use waitForBytesWritten and waitForReadyRead:
// to receive datagrams, the socket needs to be bound to an address and port
m_imuRcvSocket.bind();
// send the initial message
QByteArray message("Hi it's Charly");
qint64 bytesSent= m_imuRcvSocket.writeDatagram(message,
QHostAddress("200.0.0.3"),
10000);
bool datagramWritten = m_imuRcvSocket.waitForBytesWritten();
// add code to check datagramWritten
datagram.resize(50); // random size for testing
bool datagramReady = m_imuRcvSocket.waitForReadyRead() && m_imuRcvSocket.hasPendingDatagrams();
// add code to check datagramReady
m_imuRcvSocket.readDatagram(datagram.data(),
datagram.size(),
&sender,
&senderPort);
cout << "================="
<< "\nMessage from <" << sender << "> on port " << senderPort
<< "\nString : " << datagram
<< "\nSize: " << pendingBytes << " Bytes (characters)\n"
<< "=================" <<
endl;
A better alternative would be to use signals and slots as described in the documentation of QUdpSocket
if you plan to use the microprocessor as a client with UDP you need the MAC address of the destination machine otherwise it will not work. This one took me 4 hours to figure out.
I'm implementing a simple UDP server-client app. The main logic is: server starts sending broadcast -> clients responses -> server stops sending broadcast and performs some work.
I'm stuck with broadcasting loop:
sUDP::sUDP(QObject *parent) : QObject(parent)
{
serverSocket = new QUdpSocket(this);
serverIp = new QHostAddress("192.168.1.2");
pickUpThePhone = false;
if(serverSocket->state() != serverSocket->BoundState){
if (!serverSocket->bind(*serverIp, 4321)) {
qFatal("Error binding server");
}
}
connect(serverSocket,SIGNAL(readyRead()), SLOT(readSocket()));
while(!pickUpThePhone){
QByteArray Data;
Data.append("server");
serverSocket->writeDatagram(Data, Data.size(), QHostAddress("192.168.255.255"), 1234);
}
}
actually signal readyRead() never emitted and so broadcasting never stops:
void sUDP::readSocket()
{
qDebug() << "read";
while(serverSocket->hasPendingDatagrams()){
QByteArray buffer;
buffer.resize(serverSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
serverSocket->readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);
if(strcmp(buffer.data(),"stop") == 0){
pickUpThePhone = true;
}
}
}
The client works as it should - it responses to the datagram with "server" with "stop" message, but it looks like the while-loop is never interrupted.
Any help will be useful, thank you.
It doesn't work because your program is busy sending broadcasts. Your while loop will not let the Qt event-loop perform its work and therefore your program can't do anything else.
You're also sending broadcasts constantly, your computer will do nothing else than spam the network.
The solution to both problems is a timer. Create a repeating timer that once every second (ore more or less) sends out the broadcast. Stop or pause the timer when you get a response.
I send command to a system and read response from it. My machine is client, I write a packet successfully to server, however, I cannot receive the response from it. I am wondering why this happens, please have a look at my code: (server address is 192.168.100.143 and the port is 11000)
void UDP::UDPInit(int port)
{
socketPort = port;
udpsocket = new QUdpSocket(this);
}
void UDP::sendCommand(QByteArray data)
{
QHostAddress *host = new QHostAddress("192.168.100.143");
quint16 port = 11000;
if(udpsocket->writeDatagram(data.data(),QHostAddress(ip),socketPort)==-1)
emit clientLogMessage(QString("UDPCLIENT : Write problem !"));
else
udpsocket->flush();
while (!udpsocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(udpsocket->pendingDatagramSize());
qDebug() << udpsocket->pendingDatagramSize();
udpsocket->readDatagram(datagram.data(), datagram.size(), host, &port);
emit dataReceived(datagram);
}
}
QUdpSocket has a signal readyRead which is emitted each time a new packet is available, if you are in an event loop I suggest you use it
the condition in your while is negated which means that udpsocket->pendingDatagramSize() will return -1 inside the while loop and readDatagram will discard the packet
fixed code:
void UDP::UDPInit(int port)
{
socketPort = port;
udpsocket = new QUdpSocket(this);
connect(udpsocket, SIGNAL(readyRead()), this, SLOT(readSocket()));
}
void UDP::sendCommand(QByteArray data)
{
QHostAddress *host = new QHostAddress("192.168.100.143");
quint16 port = 11000;
if(udpsocket->writeDatagram(data.data(),QHostAddress(ip),socketPort)==-1)
emit clientLogMessage(QString("UDPCLIENT : Write problem !"));
else
udpsocket->flush();
}
void UDP::readSocket()
{
while (udpsocket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(udpsocket->pendingDatagramSize());
qDebug() << udpsocket->pendingDatagramSize();
udpsocket->readDatagram(datagram.data(), datagram.size(), host, &port);
emit dataReceived(datagram);
}
}