Qt C ++控制台服务器,等待套接字连接&同时接受输入?

时间:2013-02-04 22:46:11

标签: c++ qt sockets input

我正在将服务器编写为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();

我如何才能使套接字等待连接同时等待用户输入的命令?

1 个答案:

答案 0 :(得分:2)

您的代码问题是因为您使用while循环阻止事件循环。因此,您的问题的解决方案是异步读取标准输入。在Linux(在Mac上,我猜),根据各种互联网资源,您可以使用QSocketNotifier通知数据何时到达stdin,并手动阅读。

当我使用Windows时,我建议你这样做(这应该适用于所有平台):

  1. 打开用于从stdin
  2. 读取数据的线程
  3. 一旦获得了一些数据(可能是行?),您可以使用Qt信号槽机制将数据传递给主线程进行处理,而不会阻塞事件循环。
  4. 所以,这是伪代码。 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() signal,在构造函数中调用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()));
    }