2016-09-13 6 views
0

В настоящее время я кодирую портовый сканер в Qt (C++) для Mac. Процесс проверки того, открыт ли какой-либо порт или нет, работает полностью нормально. Но, если диапазон портов, который пользователь хочет проверить, слишком велик, каждый порт будет проверяться, но выход будет происходить только после этого процесса. Программа фактически должна проверять, например, порт 1 и выводить результат. После этого следует проверить следующее и выход и так далее ...Обновление кадра после каждой итерации

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++; 
} 

Есть ли у вас посоветовать, как я мог бы иметь выход после каждой итерации?

+1

Вы могли бы хотеть поместить вызов [ 'QApplication :: processEvents()'] (http://doc.qt.io/ qt-5/qcoreapplication.html # processEvents), чтобы дать пользовательскому интерфейсу возможность обрабатывать сообщения и показывать обновления. –

+0

@KarstenKoop Большое спасибо! Это решило мою проблему. –

+0

Ваш код блокирует gui. Прекратите блокировать gui, и все будет хорошо. Вы ** ** выходите после каждой итерации, но никогда не возвращаете * в цикл событий, поэтому пользовательский интерфейс не имеет возможности обновиться. ** Не используйте ** processEvents'. Рассматривайте любое использование этого метода как ошибку. Это асинхронный мир, не пишите код, который притворяется иначе, и вы сэкономите массу неприятностей. –

ответ

0

Простейшим решением является одновременное выполнение всего задания сканирования с использованием пула потоков. Связь между потоками осуществляется безопасно с помощью механизма сигнал-слот:

// 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

Возможно, что-то вроде этого:

//... 
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 
} 

Обратите внимание на использование HTML для форматирования части.

Это добавляет весь выход в ваше поле ПОСЛЕ цикла. Чтобы заставить его работать на каждую итерацию, просто установите msgs = ..., а не msgs += ... в цикле, а затем переместите if(tooManyPorts) ... в конце вашего for, но не снаружи. Честно говоря, мне трудно понять, не хотите ли вы первой версии (ПОСЛЕ цикла), так как прямо сейчас вы : добавляете свой вывод в текстовое поле на каждом этапе итерации.

+0

О, я хочу, чтобы результат был добавлен в текстовое поле после каждого шага итерации, но с моим кодом это не происходит на самом деле, даже если это действительно так. Но я собираюсь попробовать ваше решение. ** ОБНОВЛЕНИЕ: ** Не работает. Выход добавляется в целом, а не шаг за шагом. –

+0

Хм, можете ли вы предоставить более подробную информацию о текстовом объекте редактирования, таком как его свойства. У меня есть приложение, которое добавляет несколько строк на основе полученных событий каждые пару миллисекунд, и у меня нет проблем. Пожалуйста, уточните свой вопрос. – rbaleksandar

+0

Я решил проблему с предложением @Karsten Koop. Проблема заключалась в том, что 'append' был вызван слишком часто за короткий промежуток времени, поэтому GUI не смог обновить и обновить каждый шаг итерации. –

Смежные вопросы