How to detect "port unreachable" condition from QUdpSocket? - c++

I'm trying to use QUdpSocket to send some data to a well-defined host:port destination. When the destination port is not being listened on (e.g. the client is not running), ICMP messages appear that say "port unreachable".
I'd like to detect this condition in my program and act when it happens. I read somewhere that if I directly "connect" to the host instead of just doing "fire and forget" with my datagrams, then the application should receive these ICMP notifications. But the following code doesn't appear to get any errors.
#include <QTimer>
#include <QString>
#include <QUdpSocket>
#include <QCoreApplication>
class DataSender : public QObject
{
public:
DataSender(const QString& host, quint16 port);
void send(const char* data, unsigned size);
private:
void onSocketError(QUdpSocket::SocketError error);
private:
QUdpSocket socket;
};
DataSender::DataSender(const QString& host, const quint16 port)
{
connect(&socket, &QUdpSocket::connected, this, []{qDebug() << "connected";});
connect(&socket, &QUdpSocket::disconnected, this, []{qDebug() << "disconnected";});
connect(&socket, &QUdpSocket::hostFound, this, []{qDebug() << "hostFound";});
connect(&socket, qOverload<QUdpSocket::SocketError>(&QUdpSocket::error), this, &DataSender::onSocketError);
socket.connectToHost(host, port, QIODevice::WriteOnly);
qDebug() << "UDP server started";
}
void DataSender::send(const char*const data, const unsigned size)
{
socket.write(data, size);
}
void DataSender::onSocketError(const QUdpSocket::SocketError)
{
qDebug() << "UDP socket error:" << socket.errorString();
}
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
DataSender sender("127.0.0.1", 33333);
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&sender]{ sender.send("Test", 4); });
timer.start(1000);
return app.exec();
}
In the output I only get
hostFound
connected
UDP server started
At the same time, when running this test, I do see the "Port unreachable" ICMP messages in Wireshark. How can I actually receive and handle them?
I've found that in the Linux socket API the write(2) syscall returns -1 with errno=ECONNREFUSED in this case. But with QUdpSocket I'd like to get a cross-platform solution. I've also found that syscall-set errno does appear to be preserved after QUdpSocket::write() returns, so I might be able to use this, but this is specific to Linux, while the list of my target platforms also includes Windows.

Related

Handling TCP/IP Packet Splitting using Qt or Similar Libraries?

I am trying to send TCP messages over my local network that exceed the MTU limit of ~1500 bytes. I know that TCP protocol splits messages exceeding 1500 bytes into individual packets at some level, but am unclear if it's something I need to deal with at the application level. So I wrote a test application in ROS and Qt, C++, to test the behavior. I picked these because they are what my overall project is written in.
My test_fragmentation_test node sets up a server, then the test_client node establishes a connection and sends a message exceeding 1500 bytes. The goal is to get the server to receive this full message as one cohesive unit.
When I test these nodes on the same computer, the server receives the full messages as one unit. However, when I put the server and client on separate computers (still same network), the server receives the message as multiple packets. The packets are either 1500 bytes, or multiples thereof, so I believe they are sometimes getting squashed together in the read buffer in pairs of two or three.
Here is my code:
tcp_fragmentation_test.cpp
#include "ros/ros.h"
#include <QtCore/QCoreApplication>
#include <QTcpSocket>
#include <QTcpServer>
#include <QDebug>
//prototype functions
void initialConnect();
void receiveMessage();
//global(ish) variables
extern QTcpServer* subserver;
extern QTcpSocket* subsocket;
QTcpServer* subserver;
QTcpSocket* subsocket;
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
//initiate ROS
ros::init(argc, argv, "tcp_fragmentation_test_node");
ros::NodeHandle n;
subserver = new QTcpServer();
//call initialConnect on first connection attempt
QObject::connect(subserver, &QTcpServer::newConnection,initialConnect);
quint16 server_port = 9998;
//start listening
QHostAddress server_IP = QHostAddress("127.0.0.1");
//start listening for incoming C2 connections
if(!subserver->listen(server_IP, server_port))
{
qDebug().noquote() << "subserver failed to start on: " + server_IP.toString() + "/" + QString::number(server_port);
}
else
{
qDebug().noquote() << "subserver started on: " + server_IP.toString() + "/" + QString::number(server_port);
}
return app.exec(); //inifite loop that starts qt event listener
}
//accept incoming connection
void initialConnect()
{
//accept the incoming connection
subsocket = subserver->nextPendingConnection();
QObject::connect(subsocket, &QTcpSocket::readyRead, receiveMessage);
}
void receiveMessage()
{
//read data in
QByteArray received_message = subsocket->readAll();
qDebug() << "message received: ";
qDebug() << received_message;
}
test_client.cpp
#include "ros/ros.h"
#include <QtCore/QCoreApplication>
#include <QTcpSocket>
#include <QTcpServer>
#include <QDebug>
//function prototypes
bool initialConnect(QString server_IP, quint16 server_port);
void sendMessage(QByteArray message);
//global(ish) variables
QTcpSocket* test_socket;
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
ros::init(argc, argv, "test_client");
ros::NodeHandle n;
initialConnect("127.0.0.1",9998);
//send 1500+ byte message, to test IP fragmentation
sendMessage("<message large than 1500 bytes>");
return app.exec(); //inifite loop that starts qt event listener
}
bool initialConnect(QString server_IP, quint16 server_port)
{
//create test_socket
test_socket = new QTcpSocket();
//try to connect, then wait a bit to make sure it was successful
test_socket->connectToHost(server_IP, server_port);
if(test_socket->waitForConnected(1000))
{
return true;
}
else
{
qDebug() << "Failed to connect to server";
return false;
}
}
//send test message
void sendMessage(QByteArray message)
{
//send message
test_socket->write(message);
test_socket->waitForBytesWritten(1000);
return;
}
I talked to one of my colleagues about this, and they said that they had not run into this issue when sending messages that exceed the MTU in both Tcl scripting language, and HTML websockets. They sent me their code to check out, but unfortunately it is a small part of a really large and not well documented codebase, so I am having parsing out what they did and why they did not run into the same issues as me.
I know that I could probably avoid this issue by including identifying headers for each message, that include the length, and then combine read messages into an overall buffer until the correct number of bytes have been read. However, I'm trying to see if there's a simpler way than this, especially after my colleague telling me that he never ran into this issue. It seems like something that should be handled behind the scenes by the TCP protocol, and I'm trying to avoid dealing with it at the application level if at all possible.
Any ideas?

Trying to access to camera connected by LAN using Qt Network

I am trying to get data from a LAN-connected infrared camera.
I don't know how to approach packets from the external network.
Here's my code.
The code below was run on Qt 5.9.7 (msvc2017_64) I've changed codes from Qt UDP example. (https://www.bogotobogo.com/Qt/Qt5_QUdpSocket.php)
#include "myudp.h"
MyUDP::MyUDP(QObject *parent) :
QObject(parent)
{
// create a QUDP socket
socket = new QUdpSocket(this);
socket_cam = new QUdpSocket(this);
// The most common way to use QUdpSocket class is
// to bind to an address and port using bind()
// bool QAbstractSocket::bind(const QHostAddress & address,
// quint16 port = 0, BindMode mode = DefaultForPlatform)
socket->bind(QHostAddress::LocalHost, 1234);
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
}
void MyUDP::HelloUDP()
{
QByteArray Data;
QHostAddress camera_loc = QHostAddress("192.168.10.197");
quint16 cameraPort = 32197;
qint64 deg_num = socket->readDatagram(Data.data() ,964, &camera_loc,
&cameraPort);
qDebug() << "Deg_num: " << deg_num; //this returns -1. Therefore, it
seems it can't read any data from camera_loc.
// Sends the datagram datagram
// to the host address and at port.
// qint64 QUdpSocket::writeDatagram(const QByteArray & datagram,
// const QHostAddress & host, quint16 port)
socket->writeDatagram(Data, QHostAddress::LocalHost, 1234);
}
void MyUDP::readyRead()
{
// when data comes in
QByteArray buffer;
buffer.resize(socket->pendingDatagramSize());
QHostAddress sender = QHostAddress("192.168.10.197");
quint16 senderPort = 32197;
// qint64 QUdpSocket::readDatagram(char * data, qint64 maxSize,
// QHostAddress * address = 0, quint16 * port = 0)
// Receives a datagram no larger than maxSize bytes and stores it
// The sender's host address and port is stored in *address and *port
// (unless the pointers are 0).
socket->readDatagram(buffer.data(), buffer.size(),
&sender, &senderPort);
qDebug() << "Message from: " << sender.toString();
qDebug() << "Message port: " << senderPort;
qDebug() << "Message: " << buffer;
}
As I've seen on Wireshark, packets arrived correctly as I expected.
However, my code doesn't work as I expected. (readDatagram on camera_loc returns (-1))
According to other threads, we don't need to connect them because UDP communication doesn't need to make connections.
What I want to make with this code is as follow.
(0. Save data from the camera (192.168.10.197) on a variable using readDatagram) I am not sure this is a really necessary process...
1. Write data to buffer as written in this code (using writeDatagram function).
I could not find solutions even if I struggled.
I thought it would be easy but it wasn't ...
Any advice will be very grateful because I am a newbie to qt and UDP network.
Thanks in advance.

QUdpSocket - datagram is being received twice, why?

I am receiving a datagram twice on my QUdpSocket even though I am watching on wireshark and it is only received once. I create the socket and listen on port 11112. There is another device that emits data on this port which I am listening for. I consistently get two messages for each actual message sent. Im not sure what is causing this. Any thoughts?
Stripped down code :
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_socket = new QUdpSocket(this);
connect (m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
m_socket->bind(11112, QUdpSocket::ShareAddress);
}
MainWindow::~MainWindow()
{
delete ui;
delete m_socket;
}
void MainWindow::readPendingDatagrams()
{
QByteArray buffer;
QHostAddress sender;
quint16 port;
while(m_socket->hasPendingDatagrams())
{
int s = m_socket->pendingDatagramSize();
buffer.resize(s);
//for some reason there are two datagrams on the line.
// I have verified with wireshark that there is only one from the
// sender so not sure what is happening under the hood...
m_socket->readDatagram(buffer.data(),buffer.size(),&sender, &port);
QString source = sender.toString().split(":")[3];
if (source == "172.20.23.86")
{
qInfo() << buffer <<endl;
}
}
}
void MainWindow::onSocketStateChange(QAbstractSocket::SocketState state)
{
if ( state == QAbstractSocket::BoundState ) {
connect(m_socket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}
}
This may happen if the datagram is sent to a broadcast address, and you’re bound to all interfaces (0.0.0.0), and there are two interfaces the datagram is received on. To exclude this possibility, switch to the receiveDatagram API and dump the full details of the datagram you’ve received. My bet is that the interfaces you receive it on will be different each time.
You're also connecting the readPendingDatagrams slot potentially multiple times, and it may thus be fired multiple times, although hasPendingDatagrams should return false the second time round - so while this may be not be the problem, it is a problem that you must fix. It should only be connected once - when you construct the socket, i.e. in the constructor.
Unslander Monica is correct, it binds to all interfaces default, you can fix it by m_socket->bind(QHostAddress::LocalHost,11112);

Bad Request, Your browser sent a request that this server could not understand - Qt Websocket Server

I have two questions about this issue.
First of all I'm trying to get the following code working
socket = new QTcpSocket(this);
// I'm a little confused as to why we're connecting on port 80
// when my goal is to listen just on port 3000. Shouldn't I just
// need to connect straight to port 3000?
socket->connectToHost("localhost", 80);
if (socket->waitForConnected(3000))
{
qDebug() << "Connected!";
// send
socket->write("hello server\r\n\r\n\r\n\r\n");
socket->waitForBytesWritten(1000);
socket->waitForReadyRead(3000);
qDebug() << "Reading: " << socket->bytesAvailable();
qDebug() << socket->readAll();
socket->close();
}
else
{
qDebug() << "Not connected!";
}
But this is the error that I get:
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>400 Bad Request</title>\n</head><body>\n<h1>Bad `Request</h1>\n<p>Your browser sent a request that this server could not understand.<br />\n</p>\n<hr>\n<address>Apache/2.4.18 (Ubuntu) Server at 127.0.1.1 Port 80</address>\n</body></html>\n"`
Has anyone got any ideas about this?
Second question is: I'm trying to get a c++/Qt server working similar to a node js server. So I'm wanting to be able to access the connection requests in the browser. So when someone connects to site:3000 I will be able to catch the request and display some content. Can it be achieved with a QTcpSocket server? If so then how could I implement something like :
// I know this isn't valid c++, Just to give an idea of what I'm trying to achieve
socket.on(Request $request) {
if ($request.method() == 'GET') {
}
}
If this is achievable is there much speed gains in comparison to doing this in nodejs?
I'm personally trying to avoid js as much as possible.
if i comment the code then I can get a running program but when I try to connect on port 8000 from the browser nothing happens (just a 404 error)
updated answer:
header file:
#ifndef SOCKETTEST_H
#define SOCKETTEST_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
class SocketTest : public QTcpServer
{
public:
SocketTest(QObject *parent);
private:
QTcpSocket *client;
public slots:
void startServer(int port);
void readyToRead(void);
void incomingConnection(int socket);
};
#endif // SOCKETTEST_H
.cpp file
#include "sockettest.h"
SocketTest::SocketTest(QObject *parent) :
QTcpServer(parent)
{
this->startServer(8000);
}
void SocketTest::startServer(int port)
{
bool success = listen(QHostAddress::Any, port); // this starts the server listening on your port
// handle errors
}
void SocketTest::incomingConnection(int socket)
{
// a client has made a connection to your server
QTcpSocket *client = new QTcpSocket(this);
//client->setSocketDescription(socket);
// these two lines are important, they will direct traffic from the client
// socket to your handlers in this object
connect(client, SIGNAL(readyRead()), this, SLOT(readToRead()));
connect(client, SIGNAL(disconnect()), this, SLOT(disconnected()));
}
void SocketTest::readyToRead(void)
{
QTcpSocket *client = (QTcpSocket*)sender();
qDebug() << "Just got a connection";
// you can process requests differently here. this example
// assumes that you have line breaks in text requests
while (client->canReadLine())
{
QString aLine = QString::fromUtf8(client->readLine()).trimmed();
// Process your request here, parse the text etc
}
}
// this gives me the following error
// /user_data/projects/qt/QtServer/sockettest.cpp:47: error: no ‘void
// SocketTest::disconnected()’ member function declared in class ‘SocketTest’
void SocketTest::disconnected()
^
void SocketTest::disconnected()
{
// jsut a qu, wont all these * vars lead to a memory leak? and shouldn't I be using a var Qtc... *client; in the header file?
QTcpSocket *client = (QTcpSocket*)sender();
// clean up a disconnected user
}
Here with waitForConnected, you are connecting on port 80, and waiting 3000ms maximum for the "connected state", i.e. not connecting on port 3000 at all. This is the blocking way of waiting for a connection to be established, instead of connecting to the QTcpSocket::connected signal.
Like Yuriy pointed out, QNetworkAccessManager is way more convenient to handle HTTP requests as a client. As in your example, you created a TCP client, and not a server
Yes you can build an web server with Qt, it's a bit painfull from scratch (QTcpServer class), but several projects make it a bit easier: QHttpServer, QtWebApp
If performance is your goal, I doubt you can achieve something significantly better (or just "better") without spending a lot of time on it. Namely to be able to handle a large number of request simultaneously in a fast way, a basic implementation will not be enough.
You should subclass QTCPServer. Set it up to listen on the port you want. This object will then get the requests and you can parse them and respond to them.
Something like this (partial code);
#include <QTcpServer>
#include <QTcpSocket>
class mySuperNodeLikeServer : public QTcpServer
{
mySuperNodeLikeServer(QObject *parent);
void startServer(int port);
void readyToRead(void);
void incomingConnection(int socket);
}
// in your .cpp file
void mySuperNodeLikeServer::startServer(int port)
{
bool success = listen(QHostAddress::Any, port); // this starts the server listening on your port
// handle errors
}
void mySuperNodeLikeServer::incomingConnection(int socket)
{
// a client has made a connection to your server
QTcpSocket *client = new QTcpSocket(this);
client->setSocketDescription(socket);
// these two lines are important, they will direct traffic from the client
// socket to your handlers in this object
connect(client, SIGNAL(readyRead()), this, SLOT(readToRead()));
connect(client, SIGNAL(disconnect()), this, SLOT(disconnected()));
}
void mySuperNodeLikeServer::readyToRead(void)
{
QTcpSocket *client = (QTcpSocket*)sender();
// you can process requests differently here. this example
// assumes that you have line breaks in text requests
while (client->canReadLine())
{
QString aLine = QString::fromUtf8(client->readLine()).trimmed();
// Process your request here, parse the text etc
}
}
void mySuperNodeLikeServer::disconnected()
{
QTcpSocket *client = (QTcpSocket*)sender();
// clean up a disconnected user
}

QTcpSocket two way client-server communication

i'm developing an app for Raspberry PI based on socket interface. The main idea is that Raspberry will be connected to a sensor, collect data and send it via WiFi to Android device. From Android I can communicate with sensor sending some commands. I'm a beginner in this kind of development and following some tutorials about QTcpSocket I have created a simple client-server app but it is only in one direction. Server listens for what client is sending. Could you help me to improve it into two way communication? I've read that QTcpSocket doesn't require threading for this kind of problem but I didn't find any solution.
I would appreciate any help!
server.cpp:
#include "server.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <cstdio>
#include <QtDebug>
Server::Server(QObject *parent) :
QObject(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()),
this, SLOT(on_newConnection()));
}
void Server::listen()
{
server->listen(QHostAddress::Any, 5100);
}
void Server::on_newConnection()
{
socket = server->nextPendingConnection();
if(socket->state() == QTcpSocket::ConnectedState)
{
printf("New connection established.\n");
qDebug()<<socket->peerAddress();
}
connect(socket, SIGNAL(disconnected()),
this, SLOT(on_disconnected()));
connect(socket, SIGNAL(readyRead()),
this, SLOT(on_readyRead()));
}
void Server::on_readyRead()
{
while(socket->canReadLine())
{
QByteArray ba = socket->readLine();
if(strcmp(ba.constData(), "!exit\n") == 0)
{
socket->disconnectFromHost();
break;
}
printf(">> %s", ba.constData());
}
}
void Server::on_disconnected()
{
printf("Connection disconnected.\n");
disconnect(socket, SIGNAL(disconnected()));
disconnect(socket, SIGNAL(readyRead()));
socket->deleteLater();
}
client.cpp
#include "client.h"
#include <QTcpSocket>
#include <QHostAddress>
#include <cstdio>
Client::Client(QObject *parent) : QObject(parent)
{
socket = new QTcpSocket(this);
printf("try to connect.\n");
connect(socket, SIGNAL(connected()),
this, SLOT(on_connected()));
}
void Client::on_connected()
{
printf("Connection established.\n");
char buffer[1024];
forever
{
while(socket->canReadLine())
{
QByteArray ba = socket->readLine();
printf("from server: %s", ba.constData());
}
printf(">> ");
gets(buffer);
int len = strlen(buffer);
buffer[len] = '\n';
buffer[len+1] = '\0';
socket->write(buffer);
socket->flush();
}
}
void Client::connectToServer()
{
socket->connectToHost(QHostAddress::LocalHost, 5100);
}
From architectural point of view you should define some communication rules (message flow) between your server and client first.
Then just read(write) from(to) instance of QTCPSocket according to defined flow.
You can, for instance, read data on server side, check what you should answer and write response to the same socket from which you have read. For line oriented messages (and only for them) code could look like:
void Server::on_readyRead()
{
// "while" loop would block until at least one whole line arrived
// I would use "if" instead
if(socket->canReadLine())
{
QByteArray ba = socket->readLine();
QByteArray response;
// some code which parses arrived message
// and prepares response
socket->write(response);
}
//else just wait for more data
}
Personally, I would move parsing and sending responses out from on_readyRead() slot to avoid blocking event loop for too long time, but since you are a beginner in network programming I just wanted to clarify what could be done to implement two way communication.
For more details you can see http://qt-project.org/doc/qt-4.8/qtnetwork.html
Remember about checking if whole message has arrived on both client and server side. If you use your own protocol (not HTTP, FTP nor other standarized) you can add message length on the beginning of message.
All you need to do is to write/read from the socket from the client or server. A TCP connection is already two way connection.
You may be having some issues with your forever loop in Client. You should really use the readyRead signal on the socket in the same way that you do for the server and drop that forever loop.
Handle keyboard input in a non-blocking manner rather than using gets(). gets() will block the main thread and prevent it from running the event loop. This is not the best way to handle things in your case since you want to be able to handle data from the server and from the user at the same time.
Maybe take a look at this with respect to keyboard handling from a console app:
using-qtextstream-to-read-stdin-in-a-non-blocking-fashion
Alternatively make it a GUI app and use a QPlainTextEdit.