How to Scan Ports with TCP Client ?
-
Hi,
I have a TCP Client GUI application that sends read requests periodically once it connects to server side. For now, client and server side runs on my pc and I use my local address. I have followed and edited my application according to Fortune Client Example.There are two push buttons. First one scans available ip addresses and the other one connects to server after I write Port value on a linedit and select ip address on comboBox. Here my code so far:
widget.h:
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QAbstractSocket> #include <QDebug> #include <QHostAddress> #include <QMessageBox> #include <QString> #include <QTcpSocket> #include <QHostInfo> #include <QNetworkInterface> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private slots: void readSocket(); void discardSocket(); void displayError(QAbstractSocket::SocketError socketError); void on_pushButtonConnect_clicked(); void connected(); void enablePushButtonConnectButton(); void on_pushButtonScan_clicked(); private: Ui::Widget *ui; QTcpSocket *tcpSocket = nullptr; }; #endif // WIDGET_H
widget.cpp:
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); tcpSocket = new QTcpSocket(this); ui->pushButtonConnect->setDefault(true); ui->pushButtonConnect->setEnabled(false); connect(ui->comboBoxServerName, &QComboBox::editTextChanged, this, &Widget::enablePushButtonConnectButton); connect(ui->lineEditServerPort, &QLineEdit::textChanged, this, &Widget::enablePushButtonConnectButton); connect(tcpSocket, &QTcpSocket::connected, this, &Widget::connected); connect(tcpSocket, &QTcpSocket::readyRead, this, &Widget::readSocket); connect(tcpSocket, &QAbstractSocket::errorOccurred, this, &Widget::displayError); } Widget::~Widget() { delete ui; } void Widget::readSocket() { QByteArray buffer; buffer = tcpSocket->readAll(); qDebug() << buffer; } void Widget::displayError(QAbstractSocket::SocketError socketError) { switch (socketError) { case QAbstractSocket::RemoteHostClosedError: break; case QAbstractSocket::HostNotFoundError: QMessageBox::information(this, "QTCPClient", "The host was not found. Please check the host name and port settings."); break; case QAbstractSocket::ConnectionRefusedError: QMessageBox::information(this, "QTCPClient", "The connection was refused by the peer. Make sure QTCPServer is running, and check that the host name and port settings are correct."); break; default: QMessageBox::information(this, "QTCPClient", QString("The following error occurred: %1.").arg(tcpSocket->errorString())); break; } } void Widget::on_pushButtonConnect_clicked() { ui->pushButtonConnect->setEnabled(false); tcpSocket->abort(); tcpSocket->connectToHost(ui->comboBoxServerName->currentText(), ui->lineEditServerPort->text().toInt()); } void Widget::connected() { qDebug() << "connected"; } void Widget::enablePushButtonConnectButton() { ui->pushButtonConnect->setEnabled(!ui->comboBoxServerName->currentText().isEmpty() && !ui->lineEditServerPort->text().isEmpty()); } void Widget::on_pushButtonScan_clicked() { ui->comboBoxServerName->setEditable(true); QString name = QHostInfo::localHostName(); if (!name.isEmpty()) { ui->comboBoxServerName->addItem(name); QString domain = QHostInfo::localDomainName(); if (!domain.isEmpty()) ui->comboBoxServerName->addItem(name + QChar('.') + domain); } if (name != QLatin1String("localhost")) ui->comboBoxServerName->addItem(QString("localhost")); // find out IP addresses of this machine QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); // add non-localhost addresses for (int i = 0; i < ipAddressesList.size(); ++i) { if (!ipAddressesList.at(i).isLoopback()) ui->comboBoxServerName->addItem(ipAddressesList.at(i).toString()); } // add localhost addresses for (int i = 0; i < ipAddressesList.size(); ++i) { if (ipAddressesList.at(i).isLoopback()) ui->comboBoxServerName->addItem(ipAddressesList.at(i).toString()); } }
Now I want to scan ports between 1500-1600 with scan button and see available port values on comboBox. Then I can connect a server with fixed ip address and port value selected on comboBox.
When I enter wrong port value on linedit, client tries to connect for a while then shows error message. If I try to connect available ports between 1500-1600 in a for loop, it will take time. Also I don't think this is the way.
There is Blocking Fortune Client example that has synchronous approach. This example uses waitFor....() functions in a non-GUI thread as I understand.
So do I change my client application's asynchronous approach to syncronous approach ? Would it be the appropriate way with Blocking Fortune example to scan ports by writing something like this ?:quint16 start = 1500; quint16 end = 1600; quint16 timeoutInt = 50; for(quint16 i = start; i < end; i++){ tcpSocket.connectToHost(host, i); if(tcpSocket.waitForConnected(timeoutInt)){ qInfo() << "Open Port: " << i; QString openPort = QString::number(i); ui->comboBoxDeviceDiscoverName->addItem(openPort ); tcpSocket.disconnectFromHost(); } }
If I don't need syncronous approach with threads, could you please give me a suggestion to scan ports ? Thank you in advance.
Regards
-
@TokaraForest
I don't see that it will make any difference whether you stick with asynchronous approach (which does not block) in UI thread or use synchronous approach withwaitFor...()
in a thread (which will block that thread but not the Ui thread). Either way scanning 100 ports will take some time because each port has to have time to timeout. It will be "slow". You might as well use asynchronous and not get involved with threads if this is what you have to do.But why do you want to scan ports, and at which side? Client should be able to be set to use any available port to connect to server (this is fast). And server needs to specify which port it will be listening on, there is no point it finding a free one as client will not know which one it picked to connect on.