enter image description here
this is the code, start two process in one pc, but these process can`t communicate.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QHostInfo>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->comboTargetIp->addItem("224.0.1.0", "224.0.1.0");
ui->comboTargetIp->addItem("224.0.1.10", "224.0.1.10");
ui->comboTargetIp->addItem("224.0.2.10", "224.0.2.10");
ui->comboTargetIp->addItem("224.0.3.10", "224.0.3.10");
ui->comboTargetIp->addItem("255.255.255.255", "255.255.255.255");
LabSocketState = ui->label_3;
ui->statusBar->addWidget(LabSocketState);
QString localIP = getLocalIp();//获取IP地址
this->setWindowTitle(this->windowTitle()+"---本机IP"+localIP);
udpSocket = new QUdpSocket;
udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);//对socket进行参数设置
connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
onSocketStateChange(udpSocket->state());
connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}
void MainWindow::on_bindBtn_clicked()
{//加入组播
QString IP = ui->comboTargetIp->currentText();
groupAddress = QHostAddress(IP);
quint16 groupPort = ui->spinBox->value();//端口
if(udpSocket->bind(QHostAddress::AnyIPv4,groupPort,QUdpSocket::ShareAddress))
{
//udpSocket->joinMulticastGroup(groupAddress);//加入多播组
ui->plainTextEdit->appendPlainText("**加入组播成功");
ui->plainTextEdit->appendPlainText("组播地址IP: "+IP);
ui->plainTextEdit->appendPlainText("**绑定端口:"+QString::number(groupPort));
ui->bindBtn->setEnabled(false);
ui->stopBtn->setEnabled(true);
ui->comboTargetIp->setEnabled(true);
}
else
{
ui->plainTextEdit->appendPlainText("**绑定端口失败");
}
}
void MainWindow::on_stopBtn_clicked()
{//退出组播
udpSocket->leaveMulticastGroup(groupAddress);//退出组播
udpSocket->abort();
ui->bindBtn->setEnabled(true);
ui->stopBtn->setEnabled(false);
ui->comboTargetIp->setEnabled(true);
ui->plainTextEdit->appendPlainText("**已退出组播,解除端口绑定");
}
void MainWindow::on_btnSend_clicked()
{//发送组播消息
quint16 groupPort = ui->spinBox->value();
QString msg = ui->lineEdit->text();
QByteArray array;
array = msg.toUtf8();
udpSocket->writeDatagram(array,groupAddress,groupPort);
ui->plainTextEdit->appendPlainText("[multicst]"+msg);
ui->lineEdit->clear();
ui->lineEdit->setFocus();
}
void MainWindow::on_clearBtn_clicked()
{
ui->plainTextEdit->clear();
}
void MainWindow::on_quitBtn_clicked()
{
this->close();
}
void MainWindow::onSocketReadyRead()
{//读取数据报
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress peerAddr;
quint16 peerPort;
udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
QString str =datagram.data();
QString peerStr = "[From"+peerAddr.toString()+":"+QString::number(peerPort)+"]";
ui->plainTextEdit->appendPlainText(peerStr);
}
QString MainWindow::getLocalIp()
{//获取本机IP
QString hostName = QHostInfo::localHostName();//本机主机名
QHostInfo hostInfo = QHostInfo::fromName(hostName);
QString localIP = " ";
QList<QHostAddress> addList = hostInfo.addresses();
if(!addList.isEmpty())
{
for(int i=0;i<addList.count();++i)
{
QHostAddress aHost = addList.at(i);
if(QAbstractSocket::IPv4Protocol == aHost.protocol())
{
localIP = aHost.toString();
break;
}
}
}
//localIP = "0.0.0.0";
return localIP;
}
screenshot is the boardcast, use the 255.255.255.255 send msg.
multicast and boardcast listen and recivce maybe not same.
target is use multicast and boardcast discover other device.
use qt network to send and recvice the msg, but the wireshark catched msg type is not boardcast.
enter image description here
qtcreator has broadcast sender and reciver demo.
See EDIT1 at the end of the question for a possible solution - It would be great if somebody could comment on my interpretation, so that I can understand better what's happening
I'm writing a simple TCP client, based on QTcpSocket and managed by a QStateMachine (connect to server -> transmit data -> if disconnected for any reason, reconnect to server).
I noticed that if the connection is shut down on the server side (client is notified with RemoteHostClosedError), after reconnection the QTcpSocket write() method succeeds but no data is transmitted on the wire - nothing is received by the server, and the bytesWritten() signal on the client side does not fire up.
I found in the documentation for error() signal (https://doc.qt.io/qt-5/qabstractsocket.html#error) that
When this signal is emitted, the socket may not be ready for a reconnect attempt. In that case,
attempts to reconnect should be done from the event loop".
I think I'm already ok with that, as the reconnection happens in one of the QStateMachine states, and QStateMachine should have its own event loop according to the QT docs.
Below some simplified code to reproduce the issue (sorry, not so minimal but I could not find a simpler way to show the problem):
testclient.h
#ifndef TESTCLIENT_H
#define TESTCLIENT_H
#include <QObject>
#include <QTcpSocket>
#include <QDebug>
#include <QStateMachine>
class TestClient : public QObject
{
Q_OBJECT
public:
explicit TestClient(QObject *parent = nullptr);
public slots:
void start();
signals:
// FSM events
void fsmEvtConnected();
void fsmEvtError();
private slots:
void onSocketConnected(); // Notify connection to TCP server
void onSocketDisconnected(); // Notify disconnection from TCP server
void onSocketBytesWritten(qint64 bytes); // Notify number of bytes written to TCP server
void onSocketError(QAbstractSocket::SocketError err);
// FSM state enter/exit actions
void onfsmConnectEntered();
void onfsmTransmitEntered();
void onfsmTransmitExited();
private:
// Member variables
QTcpSocket* m_socket; // TCP socket used for communications to server
QStateMachine* m_clientFsm; // FSM defining general client behaviour
private:
void createClientFsm(); // Create client FSM
};
#endif // TESTCLIENT_H
testclient.cpp
#include "testclient.h"
#include <QState>
#include <QThread> // Sleep
//-----------------------------------------------------------------------------
// PUBLIC METHODS
//-----------------------------------------------------------------------------
TestClient::TestClient(QObject *parent) : QObject(parent)
{
m_socket = new QTcpSocket(this);
connect(m_socket, SIGNAL(connected()),this, SLOT(onSocketConnected()));
connect(m_socket, SIGNAL(disconnected()),this, SLOT(onSocketDisconnected()));
connect(m_socket, SIGNAL(bytesWritten(qint64)),this, SLOT(onSocketBytesWritten(qint64)));
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
}
void TestClient::start()
{
createClientFsm();
m_clientFsm->start();
}
//-----------------------------------------------------------------------------
// TCP CONNECTION MANAGEMENT SLOTS
//-----------------------------------------------------------------------------
void TestClient::onSocketConnected()
{
qDebug() << "connected...";
emit fsmEvtConnected();
}
void TestClient::onSocketDisconnected()
{
qDebug() << "disconnected...";
emit fsmEvtError();
}
void TestClient::onSocketBytesWritten(qint64 bytes)
{
qDebug() << bytes << " bytes written...";
}
void TestClient::onSocketError(QAbstractSocket::SocketError err)
{
qDebug() << "socket error " << err;
}
//-----------------------------------------------------------------------------
// FSM MANAGEMENT
//-----------------------------------------------------------------------------
void TestClient::createClientFsm()
{
m_clientFsm = new QStateMachine(this);
// Create states
QState* sConnect = new QState();
QState* sTransmit = new QState();
// Add transitions between states
sConnect->addTransition(this, SIGNAL(fsmEvtConnected()), sTransmit);
sTransmit->addTransition(this, SIGNAL(fsmEvtError()), sConnect);
// Add entry actions to states
connect(sConnect, SIGNAL(entered()), this, SLOT(onfsmConnectEntered()));
connect(sTransmit, SIGNAL(entered()), this, SLOT(onfsmTransmitEntered()));
// Add exit actions to states
connect(sTransmit, SIGNAL(exited()), this, SLOT(onfsmTransmitExited()));
// Create state machine
m_clientFsm->addState(sConnect);
m_clientFsm->addState(sTransmit);
m_clientFsm->setInitialState(sConnect);
}
void TestClient::onfsmConnectEntered()
{
qDebug() << "connecting...";
m_socket->connectToHost("localhost", 11000);
// Wait for connection result
if(!m_socket->waitForConnected(10000))
{
qDebug() << "Error: " << m_socket->errorString();
emit fsmEvtError();
}
}
void TestClient::onfsmTransmitEntered()
{
qDebug() << "sending data...";
m_socket->write("TEST MESSAGE");
}
void TestClient::onfsmTransmitExited()
{
qDebug() << "waiting before reconnection attempt...";
QThread::sleep(2);
}
main.cpp
#include <QCoreApplication>
#include "testclient.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
TestClient client(&a);
client.start();
return a.exec();
}
To test, you can just launch netcat (nc -l -p 11000) , then close the nc process after receiving TEST MESSAGE and finally relaunch it again. The second time, TEST MESSAGE is not received, and we don't have the onSocketBytesWritten() printout, see below:
connecting...
connected...
sending data...
12 bytes written... <<<<<<<<<< Correct transmission, event fires up
socket error QAbstractSocket::RemoteHostClosedError
disconnected...
waiting before reconnection attempt...
connecting...
connected...
sending data... <<<<<<<<<< No transmission, event does not fire up, no socket errors!
EDIT1: I found out that if I create the QTcpSocket on connection and destroy it on disconnection, the problem does not happen. Is this the expected/proper way to use sockets?
Wouldn't it be possible instead to create the socket just once and just connect/disconnect? Maybe it is just a matter of flushing or cleaning up in a specific manner, but I could not find it so far.
Here are the modifications that make the code above work on server-side disconnection:
Move socket creation from class constructor to onfsmConnectEntered() - handler for entry in the "Connect" QState:
void TestClient::onfsmConnectEntered()
{
m_socket = new QTcpSocket(this);
connect(m_socket, SIGNAL(connected()),this, SLOT(onSocketConnected()));
connect(m_socket, SIGNAL(disconnected()),this, SLOT(onSocketDisconnected()));
connect(m_socket, SIGNAL(bytesWritten(qint64)),this, SLOT(onSocketBytesWritten(qint64)));
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError)));
qDebug() << "connecting...";
m_socket->connectToHost("localhost", 11000);
// The rest of the method is the same
}
Delete the socket on disconnection, so that it is deallocated and will be created again on reconnection:
void TestClient::onSocketDisconnected()
{
qDebug() << "disconnected...";
m_socket->deleteLater();
m_socket = nullptr;
emit fsmEvtError();
}
Do not use waitForX methods as they block the event loop and prevent them from using that resource as the signals do not do their job correctly or the QStateMachine.
Considering the above, the solution is:
void TestClient::onfsmConnectEntered()
{
m_socket->connectToHost("localhost", 11000);
}
But even so your code has errors since it does not consider other cases such as:
If when you start the client the server is not running, your application will try to connect the error will be launched and nothing else.
If the server fails for a longer time than the 10000 ms timeout set to waitForConnected(), the same will happen as in the previous case.
Then the idea is to try to connect until you are sure of the connection and that can be done through a QTimer with an appropriate period.
testclient.h
#ifndef TESTCLIENT_H
#define TESTCLIENT_H
#include <QObject>
class QTcpSocket;
class QStateMachine;
class QTimer;
#include <QAbstractSocket>
class TestClient : public QObject
{
Q_OBJECT
public:
explicit TestClient(QObject *parent = nullptr);
public slots:
void start();
signals:
// FSM events
void fsmEvtConnected();
void fsmEvtError();
private slots:
void onSocketConnected(); // Notify connection to TCP server
void onSocketDisconnected(); // Notify disconnection from TCP server
void onSocketBytesWritten(qint64 bytes); // Notify number of bytes written to TCP server
void onSocketError(QAbstractSocket::SocketError err);
// FSM state enter/exit actions
void onfsmConnectEntered();
void onfsmTransmitEntered();
private:
// Member variables
QTcpSocket* m_socket; // TCP socket used for communications to server
QStateMachine* m_clientFsm; // FSM defining general client behaviour
QTimer* m_timer;
private:
void createClientFsm(); // Create client FSM
void tryConnect();
};
#endif // TESTCLIENT_H
testclient.cpp
#include "testclient.h"
#include <QState>
#include <QStateMachine>
#include <QTcpSocket>
#include <QThread> // Sleep
#include <QTimer>
//-----------------------------------------------------------------------------
// PUBLIC METHODS
//-----------------------------------------------------------------------------
TestClient::TestClient(QObject *parent) : QObject(parent)
{
m_socket = new QTcpSocket(this);
m_timer = new QTimer(this);
m_timer->setInterval(100);
connect(m_timer, &QTimer::timeout, this, &TestClient::tryConnect);
connect(m_socket, &QAbstractSocket::connected,this, &TestClient::onSocketConnected);
connect(m_socket, &QAbstractSocket::disconnected,this, &TestClient::onSocketDisconnected);
connect(m_socket, &QIODevice::bytesWritten,this, &TestClient::onSocketBytesWritten);
connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TestClient::onSocketError);
}
void TestClient::start()
{
createClientFsm();
m_clientFsm->start();
}
//-----------------------------------------------------------------------------
// TCP CONNECTION MANAGEMENT SLOTS
//-----------------------------------------------------------------------------
void TestClient::onSocketConnected()
{
m_timer->stop();
qDebug() << "connected...";
emit fsmEvtConnected();
}
void TestClient::onSocketDisconnected()
{
qDebug() << "disconnected...";
emit fsmEvtError();
}
void TestClient::onSocketBytesWritten(qint64 bytes)
{
qDebug() << bytes << " bytes written...";
}
void TestClient::onSocketError(QAbstractSocket::SocketError err)
{
qDebug() << "socket error " << err;
}
//-----------------------------------------------------------------------------
// FSM MANAGEMENT
//-----------------------------------------------------------------------------
void TestClient::createClientFsm()
{
m_clientFsm = new QStateMachine(this);
// Create states
QState* sConnect = new QState();
QState* sTransmit = new QState();
// Add transitions between states
sConnect->addTransition(this, SIGNAL(fsmEvtConnected()), sTransmit);
sTransmit->addTransition(this, SIGNAL(fsmEvtError()), sConnect);
// Add entry actions to states
connect(sConnect, &QAbstractState::entered, this, &TestClient::onfsmConnectEntered);
connect(sTransmit, &QAbstractState::entered, this, &TestClient::onfsmTransmitEntered);
// Create state machine
m_clientFsm->addState(sConnect);
m_clientFsm->addState(sTransmit);
m_clientFsm->setInitialState(sConnect);
}
void TestClient::tryConnect(){
m_socket->connectToHost("localhost", 11000);
}
void TestClient::onfsmConnectEntered()
{
m_timer->start();
}
void TestClient::onfsmTransmitEntered()
{
qDebug() << "sending data...";
m_socket->write("TEST MESSAGE");
}
Trying to implement a multi thread server in Qt, just consider this:
in SocketThread class:
SocketThread::SocketThread(qintptr descriptor, QObject *parent)
: QThread(parent), socketDescriptor(descriptor)
{
socket = new QTcpSocket();
socket->setSocketDescriptor(socketDescriptor);
socket->moveToThread(this);
connect(socket, &QTcpSocket::readyRead, [this]() { qDebug() << socket->readAll(); }); //trying to read messages from clients
}
in Connection class: creating thread in incomingConnection()
void Connection::incomingConnection(qintptr socketDescriptor)
{
SocketThread *socketThread = new SocketThread(socketDescriptor);
socketThread->start();
connect(socketThread, &SocketThread::started, [&]() { socketThread->socket->write("Hello!"); }); //write a message to client when thread is created
}
Strange thing is, if I add this line:
connect(socketThread, &SocketThread::started, & { socketThread->socket->write("Hello!"); }); //write a message to client when thread is created
Then the socket will not read message from client. If I remove that line, the socket will read message from client.
I want the server to send message to client when a thread is created and to read message from client as well. How can do solve the problem?
Edit:
SocketThread header file:
class SocketThread : public QThread
{
Q_OBJECT
public:
SocketThread(qintptr descriptor, QObject *parent = 0);
~SocketThread();
QTcpSocket *socket;
qintptr socketDescriptor;
};
in the Connection constructor:
Connection::Connection(QObject *parent) : QTcpServer(parent)
{
this->listen(QHostAddress::Any, 6666);
}
And creating a connection object in the main.cpp file. That's really like all the code.
I have a program that sends and receives broadcast messages. If I run two copies of these programs on the same PC, everything works just fine. If I run each copy on different PCs, then the broadcast messages are not received. I utilized Wireshark to verify whether the packets were sent (yes they were). In addition, on PC "A" Wireshark does not observe the sent packets, but on PC "B" everything is OK. Could the problem be in hardware?
Receiver code:
Receiver::Receiver(QObject *parent) : QObject(parent)
{
udpSocket = new QUdpSocket(this);
udpSocket->bind(45454, QUdpSocket::ShareAddress);
timer = new QTimer(this);
timer->start(500);
connect(timer, SIGNAL(timeout()), this, SLOT(processPendingDatagrams()));
}
void Receiver::processPendingDatagrams()
{
qDebug("first entrance");
while (udpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size());
qDebug() << datagram.data();
qDebug("Receive");
sendToQML(datagram.data());
}
}
Sender code:
Sender::Sender(QObject *parent) : QObject(parent)
{
messageNo = 0;
udpSocket = new QUdpSocket(this);
}
void Sender::broadcastDatagram(int message)
{
QByteArray datagram = QByteArray::number(message);
udpSocket->writeDatagram(datagram.data(), datagram.size(),
QHostAddress::Broadcast, 45454);
}
void Sender::sendBroadcast(int message)
{
qDebug() << message;
broadcastDatagram(message);
}
UPDATE
I managed to send and receive packets through an ethernet connection (via a cable).
I get the error:
QIODevice::write (QTcpSocket): device not open.
After trying , I think problem is passing parameter server->nextPendingConnection() into object. Can someone has idea how to do it correctly?
My understanding is that object for socketClient is not initialised properly.
I'm using Ubuntu with Qt.
I am implementing server using Qt. The server part has two classes based on QTcpServer and QTcpSocket.
say Server and SocketClient.
I am creating object of SocketClient in server and for testing purpose I opened telnet session and wants to see that server write "hello" on terminal. But somehow its not working. Can someone please advice me where I am making mistake.
Server::Server(QObject *parent) : QObject(parent)
{
server_obj = new QTcpServer( this );
}
void Server::startServer()
{
connect( server_obj, SIGNAL( newConnection() ), this, SLOT( incomingConnection() ) );
if( !server_obj->listen( QHostAddress::Any, 9999) )
{
qDebug() << " Server failed to get started";
}
else
{
qDebug() << " Server started"; // this is successful
}
}
void Server::incomingConnection()
{
socketforClient = new SockClient( server_obj->nextPendingConnection() );// has a doubt on nextPendingconection??
//only for testing remove it
socketforClient->writeToClient();
}
Class for Client
* Description: Its a constructor.I have changed default constructor to add QTcpSocket* object in parameter.I used this constructor in void Server::incomingConnection()
*/
SockClient::SockClient(QObject *parent,QTcpSocket* socket ) : QObject(parent)
{
socketClient = new QTcpSocket( socket );
qDebug() << " we are in constructor of 1st sockclient ";
}
// this is for testing purpose only
void SockClient::writeToClient()
{
socketClient->write(" hello world\r\n");
socketClient->flush();
socketClient->waitForBytesWritten(3000);
}
//header file of SockClient
class SockClient : public QObject
{
Q_OBJECT
public:
explicit SockClient( QObject *parent, QTcpSocket* socket= 0 ); // I have created
void writeToClient(); // This is for testing purpose
~SockClient();
signals:
private slots:
void readClient();
private:
void sendResponsetoMops();
QTcpSocket *socketClient;
quint16 nextBlockSize;
public slots:
};
You use:
socketClient = new QTcpSocket( socket );
Try to use following code instead:
socketClient = socket;
And use
socketforClient = new SockClient(this, server->nextPendingConnection() );
instead of
socketforClient = new SockClient( server->nextPendingConnection() );