2

サーバーを Qt コンソール アプリケーションとして作成しています。ソケット接続を待機するようにサーバーをセットアップしましたが、ユーザーがサーバーを管理するためにサーバーにコマンドを入力できるようにする必要もあります。どちらも独立して活動しています。ただし、私が遭遇した問題は、入力コマンドを受け入れて処理する while ループにいるときに、サーバーが接続を受け入れないことです。

Socket クラスがあり、そのコンストラクターには次のものがあります。

connect(server,SIGNAL(newConnection()),this, SLOT(newConnection()));

コンストラクターのすぐ下で、ユーザーからコマンドを取得するためのより詳細なバージョンを持つ関数を呼び出します。

QTextStream qin(stdin, QIODevice::ReadOnly);
QString usrCmd;

while(usrCmd != "exit" && usrCmd != "EXIT") {   
    //Get command input and process here
}

newConnection() 内では、次の接続を受け入れるだけで、ソケットを使用します。

QTcpSocket *serverSocket = server->nextPendingConnection();

ソケットが接続を待機し、同時にユーザー入力コマンドを待機できるようにするにはどうすればよいですか?

4

1 に答える 1

2

コードの問題は、whileループでイベントループをブロックしているためです。したがって、問題の解決策は、stdinから非同期で読み取ることです。Linux(およびMacでは、おそらくQSocketNotifier)では、さまざまなインターネットソースに従って、データがstdinに到着したときに通知し、手動で読み取るために使用できます。

私はWindowsを使用しているので、次のようにすることをお勧めします(すべてのプラットフォームで機能するはずです)。

  1. stdinからデータを読み取るためのスレッドを開きます
  2. データ(おそらく行?)を取得したら、Qtシグナルスロットメカニズムを使用して、イベントループをブロックせずにデータをメインスレッドに渡して処理できます。

つまり、これは擬似コードです。MainAppClassは、既存のサーバークラスである必要があります。コンストラクターを編集して新しいスレッドを作成し、データを処理するための新しいスロットを追加します。

class Reader: public QThread
{
    Q_OBJECT
 public:
    Reader(QObject * parent = 0 ): QThread(parent){}

    void run(void)
    {
         forever{
             std::string data;
             std::getline (std::cin, data);

             if(data == "exit")
             {
                 emit exitServer();
                 return;
             }


             emit dataReady(QString::fromStdString(data));
         }
     }

signals:
    void dataReady(QString data);
    void exitServer();

};



class MainAppClass: public QObject
{
    Q_OBJECT
public:
    MainAppClass()
    {
        Reader * tr = new Reader(this);
        connect(tr, SIGNAL(dataReady(QString)), this, SLOT(processData(QString)));
        connect(tr, SIGNAL(exitServer()), this, SLOT(exitServer()));
        tr->start();
    }

public slots:
    void processData(QString data)
    {

        std::cout << "Command: " << data.toStdString() << std::endl;
    }

    void exitServer()
    {
       std::cout << "Exiting..." << std::endl;
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainAppClass myapp; //your server

    app.exec();
    return 0;
}

QTcpSocketの使い方の簡単なガイドラインを書いたので、ここに簡単な説明があります

クライアントを取得したら、信号をスロットにQTcpSocket接続し、オブジェクトからデータを読み取ります。コンストラクターで何も読み取る必要はありません。readyRead()sender()

読むためにあなたは標準的なQIODevice機能を使うことができます。

注:これは擬似コードであり、いくつかの変更が必要になる場合があります(読み取り時のストリームの状態の確認、リスト内のソケットへのポインターの保存、シグナルのサブスクライブ、コンストラクターのdisconnected()呼び出し、リッスンしているかどうかの確認など)。listen()QTcpServer

onReadyRead()したがって、クラスに次のコードを持つスロットが必要です。

void Server::readyReadSlot()
{
    QTcpSocket *client = (QTcpSocket*)sender(); // get socket which emited the signal
    while(client->canReadLine()) // read all lines! 
                                 // If there is not any lines received (you may not always receive 
                                 // whole line as TCP is stream based protocol),
                                 // you will not leave data in the buffer for later processing.
    {
        QString line = client->readLine(); 
        processLine(line); // or emit new signal if you like
    }
}

内部では、信号をスロットnewConnection()に接続する必要があります。readyRead()

void Server::newConnection()
{
    QTcpSocket *clientSocket = server->nextPendingConnection(); 
    connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
}
于 2013-02-04T23:04:52.430 に答える