QTcpSocket双向客户端-服务器通信

4
我正在开发一个基于套接字接口的树莓派应用程序。主要思路是将树莓派连接到传感器,收集数据并通过WiFi发送到Android设备。从Android设备上,我可以发送一些命令与传感器进行通信。我是这种开发的初学者,正在按照QTcpSocket的一些教程创建一个简单的客户端-服务器应用程序,但它只能实现单向通信。服务器只监听客户端所发送的内容。你能帮助我改进成双向通信吗?我已经阅读了QTcpSocket不需要线程来解决这种问题,但我没有找到任何解决方案。
我会非常感激任何帮助!
server.cpp:
#include "server.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <cstdio>
#include <QtDebug>

Server::Server(QObject *parent) :
QObject(parent)
{
    server = new QTcpServer(this);
    connect(server, SIGNAL(newConnection()),
    this, SLOT(on_newConnection()));
}

void Server::listen()
{
    server->listen(QHostAddress::Any, 5100);
}

void Server::on_newConnection()
{
    socket = server->nextPendingConnection();

    if(socket->state() == QTcpSocket::ConnectedState)
    {
        printf("New connection established.\n");
        qDebug()<<socket->peerAddress();
    }
    connect(socket, SIGNAL(disconnected()),
    this, SLOT(on_disconnected()));
    connect(socket, SIGNAL(readyRead()),
    this, SLOT(on_readyRead()));
}

void Server::on_readyRead()
{
    while(socket->canReadLine())
    {
        QByteArray ba = socket->readLine();

        if(strcmp(ba.constData(), "!exit\n") == 0)
        {
            socket->disconnectFromHost();
            break;
        }
        printf(">> %s", ba.constData());
    }
}

void Server::on_disconnected()
{
    printf("Connection disconnected.\n");
    disconnect(socket, SIGNAL(disconnected()));
    disconnect(socket, SIGNAL(readyRead()));
    socket->deleteLater();
}

client.cpp

#include "client.h"
#include <QTcpSocket>
#include <QHostAddress>
#include <cstdio>

Client::Client(QObject *parent) : QObject(parent)
{
    socket = new QTcpSocket(this);
    printf("try to connect.\n");
    connect(socket, SIGNAL(connected()),
    this, SLOT(on_connected()));
}

void Client::on_connected()
{
    printf("Connection established.\n");
    char buffer[1024];
    forever
    {
        while(socket->canReadLine())
        {
            QByteArray ba = socket->readLine();
            printf("from server: %s", ba.constData());
        }
        printf(">> ");
        gets(buffer);
        int len = strlen(buffer);
        buffer[len] = '\n';
        buffer[len+1] = '\0';
        socket->write(buffer);
        socket->flush();
    }

}

void Client::connectToServer()
{
    socket->connectToHost(QHostAddress::LocalHost, 5100);
}
2个回答

6

从架构角度来看,你应该首先定义一些服务器和客户端之间的通信规则(消息流)。

然后根据所定义的流从(到)QTCPSocket实例中读取(写入)数据。

例如,在服务器端读取数据后,可以检查应该回复什么,并将响应写入与读取数据相同的套接字。对于基于行的消息(仅限于它们),代码可能如下所示:

    void Server::on_readyRead()
    {
        // "while" loop would block until at least one whole line arrived
        // I would use "if" instead
        if(socket->canReadLine()) 
        {
            QByteArray ba = socket->readLine();

            QByteArray response;

            // some code which parses arrived message
            // and prepares response

            socket->write(response);
        }
        //else just wait for more data
    }

个人而言,我会将解析和发送响应从on_readyRead()槽中移出,以避免阻塞事件循环时间过长,但由于您是网络编程的初学者,我只想澄清可以实现双向通信的内容。

更多详情请参见http://qt-project.org/doc/qt-4.8/qtnetwork.html

记得在客户端和服务器端都检查是否已经到达了整个消息。如果您使用自己的协议(不是HTTP、FTP或其他标准化协议),则可以在消息开头添加消息长度。


非常感谢您的输入!我已经成功完成了,但现在我有另一个问题。我的on_readyRead()在客户端和服务器上现在是这样的:
if(socket->canReadLine()){
QByteArray ba = socket->readLine();
printf(">> %s\n", ba.constData());
char buffer[] = "server to client";
socket->write(buffer);
}
问题在于当我只想从客户端向服务器发送响应时,应用程序会停止并等待来自客户端和readyRead()的数据。我猜我应该将服务器端的写入移到其他地方。最好的地方在哪里?
- Krzysztof Jackowski
1
@lycha90 当等待readyRead()信号时,您的应用程序不会停止。当套接字中有一些准备好读取的数据时,它会发送信号,然后调用您的on_readyRead()槽函数。您可以读取数据,发送响应(如果不需要则不发送),然后控制返回到主事件循环,在此期间您的应用程序可以处理其他事件。在QT中,这被称为“非阻塞”行为。 - undercover
好的,我明白了。问题是我需要服务器一直发送数据,而客户端只需要偶尔发送。因此,我不能在服务器端的on_readyRead()方法中编写,因为它只会在客户端发送内容时执行。我的问题是,在服务器端应该在哪里编写数据?是否有任何可用于此问题的插槽? - Krzysztof Jackowski
@lycha90 没有所谓的“通用”解决方案。首先,这取决于您的数据提供程序是什么。在简单情况下,您的服务器可以是数据提供程序本身。然后,您将不得不将套接字存储在 on_newConnection() 插槽中,即作为服务器成员实现数据收集功能,并将收集到的数据写入存储的套接字。更好的解决方案是从 QObject 继承的单独数据提供程序。您可以使用信号-插槽模型实现它们之间的通信。 - undercover
感谢你的时间和帮助!我想信号槽模型对我来说是可用的。 - Krzysztof Jackowski

1
你只需要从客户端或服务器的套接字中读取/写入即可。TCP连接已经是双向连接。
你的“Client”中的“forever”循环可能会出现一些问题。你应该真正使用套接字上的“readyRead”信号,就像在服务器上一样,并放弃那个“forever”循环。
以非阻塞方式处理键盘输入,而不使用“gets()”。“gets()”将阻塞主线程并防止其运行事件循环。在你的情况下,这不是处理事物的最佳方式,因为你想同时处理来自服务器和用户的数据。
也许从控制台应用程序角度看,可以查看以下有关键盘处理的内容: 使用QTextStream以非阻塞方式读取标准输入 或者将其设置为GUI应用并使用“QPlainTextEdit”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接