2016-09-13 9 views
0

Ich schreibe gerade einen Port-Scanner in Qt (C++) für Mac. Die Überprüfung, ob ein bestimmter Port offen ist oder nicht, funktioniert einwandfrei. Aber Wenn der Portbereich, den der Benutzer überprüfen möchte, zu groß ist, wird jeder Port überprüft, aber die Ausgabe erfolgt erst nach diesem Vorgang. Das Programm sollte beispielsweise Port 1 überprüfen und das Ergebnis ausgeben. Danach sollte es das nächste und Ausgang zu überprüfen und so weiter ...Frame nach jeder Iteration aktualisieren

void MainWindow::checkPort(int portmin, int portmax, string ip) { 
int dif = portmax - portmin; 
if (dif <= 0) 
    return; 

unsigned int open = 0; 
unsigned int closed = 0; 
int checked = 0; 

sockaddr_in addr; 
addr.sin_family = AF_INET; 
addr.sin_addr.s_addr = inet_addr(ip.c_str()); 

for (int i = portmin; i <= portmax; i++) { 
    int s = socket(AF_INET, SOCK_STREAM, 0); 
    addr.sin_port = htons(i); 

    int con = ::connect(s, reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr)); 

    if (con == 0){ 
     ui->textEdit->setTextColor(Qt::green); 
     ui->textEdit->append("Port " + QString::number(i) + " open."); 
     open++; 
    } 

    if (con == -1) { 
     ui->textEdit->setTextColor(Qt::red); 
     ui->textEdit->append("Port " + QString::number(i) + " closed."); 
     closed++; 
    } 

    ::close(con); 
    ::close(s); 
    checked++; 
} 

Haben Sie bekam jede beraten, wie ich einen Ausgang nach jeder Iteration haben könnte?

+1

Sie können einen Anruf auf [ 'QApplication :: process()'] setzen wollen (http://doc.qt.io/ qt-5/qcoreapplication.html # processEvents) in Ihre Schleife, um der Benutzeroberfläche die Möglichkeit zu geben, Nachrichten zu verarbeiten und Aktualisierungen anzuzeigen. –

+0

@KarstenKoop Vielen Dank! Das hat mein Problem gelöst. –

+0

Ihr Code blockiert die GUI. Hör auf, die GUI zu blockieren und alles wird gut. Sie ** werden nach jeder Iteration ausgegeben, aber Sie * kehren * niemals zur Ereignisschleife zurück, sodass die Benutzeroberfläche keine Möglichkeit hat, sich selbst zu aktualisieren. ** Benutze 'processEvents' nicht **. Behandle jede Verwendung dieser Methode als Fehler. Es ist eine asynchrone Welt, schreibe keinen Code, der etwas anderes vorgibt und du ersparst dir jede Menge Ärger. –

Antwort

0

Die einfachste Lösung besteht darin, den gesamten Scan-Job gleichzeitig mit dem Thread-Pool auszuführen. Die Inter-Thread-Kommunikation wird sicher über den Signal-Slot-Mechanismus erfolgt:

// https://github.com/KubaO/stackoverflown/tree/master/questions/async-portscan-39469180 
#include <QtWidgets> 
#include <QtConcurrent> 
#include <sys/socket.h> 
#include <netinet/ip.h> 
#include <arpa/inet.h> 
#include <unistd.h> 

class Scanner : public QObject { 
    Q_OBJECT 
    bool running = false, stop = false; 
    int open = 0, closed = 0, total = 0; 
    void scan() { 
     running = true; 
     sockaddr_in addr; 
     addr.sin_family = AF_INET; 
     addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
     for (int i = 1; i < 65536 && !stop; ++i) { 
      auto s = socket(AF_INET, SOCK_STREAM, 0); 
      addr.sin_port = htons(i); 
      auto con = ::connect(s, reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr)); 
      emit hasResult(i, con == 0); 
      con == 0 ? ++open : ++closed; 
      ++total; 
      ::close(s); 
     } 
     emit done(); 
     running = false; 
    } 
public: 
    ~Scanner() { 
     stop = true; 
     while (running); 
    } 
    Q_SIGNAL void hasResult(int port, bool open); 
    Q_SIGNAL void done(); 
    Q_SLOT void start() { 
     QtConcurrent::run(this, &Scanner::scan); 
    } 
}; 

int main(int argc, char ** argv) { 
    using Q = QObject; 
    QApplication app{argc, argv}; 
    QWidget ui; 
    QVBoxLayout layout{&ui}; 
    QTextBrowser log; 
    QProgressBar bar; 
    QPushButton scan{"Scan localhost"}; 
    layout.addWidget(&log); 
    layout.addWidget(&bar); 
    layout.addWidget(&scan); 
    bar.setRange(1, 65535); 
    ui.show(); 

    Scanner scanner; 
    Q::connect(&scan, &QPushButton::clicked, &scanner, [&]{ 
     scan.setEnabled(false); 
     scanner.start(); 
    }); 
    Q::connect(&scanner, &Scanner::hasResult, &log, [&](int port, bool isOpen){ 
     bar.setValue(port); 
     if (!isOpen) return; 
     auto color = isOpen ? QStringLiteral("green") : QStringLiteral("red"); 
     auto state = isOpen ? QStringLiteral("open") : QStringLiteral("closed"); 
     log.append(QStringLiteral("<font color=\"%1\">Port %2 is %3.</font><br/>"). 
        arg(color).arg(port).arg(state)); 
    }); 
    Q::connect(&scanner, &Scanner::done, &scan, [&]{ 
     bar.reset(); 
     scan.setEnabled(true); 
    }); 
    return app.exec(); 
} 
#include "main.moc" 
1

Vielleicht so etwas wie dieses:

//... 
bool tooManyPorts = dif > 10000; // Set flag to true if port range is too big (for example more than 10 000 ports 
// 
QString msgs = ""; 
for (int i = portmin; i <= portmax; i++) { 
    int s = socket(AF_INET, SOCK_STREAM, 0); 
    addr.sin_port = htons(i); 

    if (con == 0){ 
     if (tooManyPorts) { 
      QString("<font color='green'>Port " + QString::number(i) + " open.</font><br/>"); 
     } 
     else { 
      ui->textEdit->setTextColor(Qt::green); 
      ui->textEdit->append("Port " + QString::number(i) + " open."); 
     } 
     open++; 
    } 

    if (con == -1) { 
     if (tooManyPorts) { 
      msgs += QString("<font color='red'>Port " + QString::number(i) + " closed.</font><br/>"); 
     } 
     else { 
      ui->textEdit->setTextColor(Qt::red); 
      ui->textEdit->append("Port " + QString::number(i) + " closed."); 
     } 
     closed++; 
    } 
    // ... 
} 
if(tooManyPorts) { 
    ui->textEdit->append(msgs); // Add all iteration messages to text edit 
} 

Beachten Sie die Verwendung von HTML für die Formatierung Teil.

Dies fügt die gesamte Ausgabe nach der Schleife zu Ihrem Feld hinzu. Um es für jede Iteration in Betrieb zu nehmen, setzen Sie einfach msgs = ... und nicht msgs += ... in die Schleife und bewegen Sie dann die am Ende Ihrer for, aber nicht außerhalb. Ehrlich gesagt, habe ich Schwierigkeiten zu verstehen, ob Sie nicht die erste Version (nach der Schleife) wollen, da Sie jetzt sind, die Ihre Ausgabe zu Ihrem Textfeld in jedem Iterationsschritt hinzufügt.

+0

Oh, ich möchte, dass die Ausgabe nach jedem Iterationsschritt zum Textfeld hinzugefügt wird, aber mit meinem Code passiert es in Wirklichkeit nicht, selbst wenn es eigentlich so wäre. Aber ich werde Ihre Lösung ausprobieren. ** UPDATE: ** Funktioniert nicht so gut. Die Ausgabe wird als Ganzes und nicht Schritt für Schritt hinzugefügt. –

+0

Hmm, können Sie mehr Informationen über Ihr Text bearbeiten Objekt wie seine Eigenschaften. Ich habe eine Anwendung, die alle paar Millisekunden basierend auf empfangenen Ereignissen mehrere Strings hinzufügt, und ich habe kein Problem. Bitte verfeinern Sie Ihre Frage. – rbaleksandar

+0

Ich habe das Problem mit @Karsten Koops Vorschlag gelöst. Das Problem war, dass 'append' in kurzer Zeit zu oft aufgerufen wurde, so dass die GUI nicht mit jedem Iterationsschritt aktualisiert und aktualisiert werden konnte. –

Verwandte Themen