如何在Qt中编写Client-Server应用程序并实现简单的协议

时间:2013-09-10 15:24:43

标签: c++ qt client-server sample

也许这是一个愚蠢的问题,实际上它的吸引力,或者Qt只是让我感到困惑。 这是事情: 在编写客户端 - 服务器应用程序时,我习惯于java,而且非常简单。我想在C ++中做同样的事情(我对C ++本身非常熟悉),我选择学习Qt。我尝试在qt中编写一些应用程序,但部分成功。

困扰我的第一件事是信号和插槽。我知道如何在GUI编程中使用它们,但它让我对网络感到困惑。阻塞有问题。当我在java中调用BufferedReader的readLine()方法时,它会阻塞,直到它从套接字连接接收到行。在Qt中,我必须确保每次都有可用的行,并在没有行时处理它。

当我将QSocket的错误信号连接到我的一些自定义插槽时,当服务器发送最后一行并关闭连接时会发出信号,而在客户端的插槽/功能中读取我从未读过最后一行。这是我到目前为止遇到的一些问题。

当我必须实现最简单的协议时,插槽并检查是否有可用的数据让我感到困惑。

重要部分:

我试图在互联网上找到一个很好的例子,但问题是所有的例子都是复杂的。有没有人可以告诉我如何编写简单的客户端 - 服务器应用程序。服务器只接受一个客户端。客户端发送包含命令的文本行。如果命令是“ADD”或“SUB”,则服务器发送“SUP”表示支持该命令。否则它发送“UNS”并关闭连接。如果客户收到“SUP”,它会发送到包含要减去或添加的数字的更多行。服务器响应结果并关闭连接。

我知道C ++需要更多编码,但在Java中这只需要5分钟,因此在C ++中编写它也不需要花很长时间。

我确信这个例子对于想要在Qt中学习网络的人来说非常有价值。

修改 这是我尝试制作应用程序(如上所述): 这是服务器部分:

 #ifndef TASK_H
#define TASK_H

#include <QObject>
#include <QTcpServer>

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



public slots:
    void run();
    void on_newConnection();
    void on_error(QAbstractSocket::SocketError);

signals:
    void finished();

 private:
    QTcpServer server;
};


#endif // TASK_H



void Task::run()
{
    connect(&server,SIGNAL(newConnection()),this,SLOT(on_newConnection()));
    connect(&server,SIGNAL(acceptError(QAbstractSocket::SocketError)),this,SLOT(on_error(QAbstractSocket::SocketError)));
    if(server.listen(QHostAddress::LocalHost, 9000)){
        qDebug() << "listening";
    }else{
        qDebug() << "cannot listen";
        qDebug() << server.errorString();
    }

}

    void Task::on_newConnection(){
        std::cout << "handeling new connection...\n";
        QTcpSocket* socket = server.nextPendingConnection();
        QTextStream tstream(socket);


        while(!socket->canReadLine()){
            socket->waitForReadyRead((-1));
        }

        QString operation = tstream.readLine();
        qDebug() << "dbg:" << operation;
        if(operation != "ADD" && operation != "SUB"){
            tstream << "UNS\n";
            tstream.flush();
            socket->disconnect();
            return;
        }
        tstream << "SUP\n";
        tstream.flush();
        double op1,op2;
        while(!socket->canReadLine()){
            socket->waitForReadyRead((-1));
        }
        op1 = socket->readLine().trimmed().toDouble();
        qDebug() << "op1:" << op1;
        while(!socket->canReadLine()){
            socket->waitForReadyRead(-1);
        }
        op2 = socket->readLine().trimmed().toDouble();
        qDebug() << "op2:" << op2;
        double r;
        if(operation == "ADD"){
            r = op1 + op2;
        }else{
            r = op1 - op2;
        }

        tstream << r << "\n";
        tstream.flush();
        qDebug() << "result is: " << r;
        socket->disconnect();

    }

    void Task::on_error(QAbstractSocket::SocketError ){
        qDebug() << "server error";
        server.close();
    }

这是客户端(标题与服务器类似,所以我不会发布):

void Task::run()
{


    QTcpSocket socket;
    std::string temp;
    socket.connectToHost(QHostAddress::LocalHost,9000);

    if(socket.waitForConnected(-1))
        qDebug() << "connected";
    else {
        qDebug() << "cannot connect";
        return;
    }

    QTextStream tstream(&socket);

    QString op;
    std::cout << "operation: ";
    std::cin >> temp;
    op = temp.c_str();
    tstream << op << "\n";
    tstream.flush();
    qDebug() << "dbg:" << op << "\n";

    while(!socket.canReadLine()){
        socket.waitForReadyRead(-1);
    }
    QString response = tstream.readLine();
    qDebug() << "dbg:" << response;
    if(response == "SUP"){

        std::cout << "operand 1: ";
        std::cin >> temp;
        op = temp.c_str();
        tstream << op + "\n";

        std::cout << "operand 2: ";
        std::cin >> temp;
        op = temp.c_str();
        tstream << op + "\n";

        tstream.flush();

        while(!socket.canReadLine()){
            socket.waitForReadyRead(-1);
        }
        QString result = tstream.readLine();

        std::cout << qPrintable("result is: " + result);

    }else if(response == "UNS"){
        std::cout << "unsupported operatoion.";
    }else{
        std::cout << "unknown error.";
    }
    emit finished();
}
  1. 我能做得更好吗?
  2. 在类似情况下有哪些好的做法?
  3. 当使用阻塞(不是信号/插槽机制)时,当另一方关闭连接时,处理事件的最佳方法是什么?
  4. 有人可以重写这个以使它看起来更专业(我只是看看它应该是什么样子,因为我认为我的解决方案远非完美)?
  5. 有人可以使用信号和插槽重写吗?
  6. 谢谢你。 抱歉我的英语,可能是愚蠢的:))

1 个答案:

答案 0 :(得分:6)

与Qt建立联系并不困难。

两个点之间的通信由单个类处理;在TCP / IP的情况下,这将是QTcpSocket类。客户端和服务器都将与QTcpSocket对象通信。

与服务器的唯一区别是你从一个QTcpServer对象开始并调用listen()来等待连接......

QTcpServer* m_pTcpServer = new QTcpServer

//create the address that the server will listen on
QHostAddress addr(QHostAddress::LocalHost); // assuming local host (127.0.0.1)

// start listening
bool bListening = m_pServer->listen(addr, _PORT); //_PORT defined as whatever port you want to use

当服务器从客户端QTcpSocket接收连接时,它将通过 newConnection 信号通知您,因此假设您已经连接到您自己类中的套接字以接收该信号信号,我们可以得到服务器QTcpSocket对象与客户端进行通信...

QTcpSocket* pServerSocket = m_pServer->nextPendingConnection();

服务器将为每个连接接收QTcpSocket对象。服务器套接字现在可用于使用 write 方法将数据发送到客户端套接字...

pServerSocket->write("Hello!");

当套接字(客户端或服务器)接收数据时,它会发出 readyRead 信号。因此,假设您已连接到套接字的readyRead信号,则插槽函数可以检索数据...

QString msg = pSocket->readAll();

您需要的其他代码是处理连接,断开连接和错误信号,您应该连接相关的插槽以接收这些通知。

确保只有在知道已建立连接后才发送数据。通常情况下,我会让服务器收到一个连接并发送一个“你好”。消息回到客户端。一旦客户端收到消息,它就知道它可以发送到服务器。

当任何一方断开连接时,剩余的一方将收到断开信号,并且可以采取适当的行动。

对于客户端,它只有一个QTcpSocket对象,在调用 connectToHost 之后,如果连接成功,您将收到已连接的信号,或者错误信号。

最后,如果您只是尝试在同一台计算机上的进程之间进行通信,则可以以相同的方式使用QLocalServer和QLocalSocket。