25

我有一个 QTcpSocket,我正在读一个循环。每次读取完整的数据包或出现错误时,我都会手动检查循环内套接字的状态,其中:

    while(true){
    if(socket->state()==QAbstractSocket::ConnectedState){
        qDebug()<<"Socket status: connected. Looking for packets...";
        if(socket->waitForReadyRead(2000)){
        //...
    }

当我执行de程序时,一旦连接并且循环开始,它总是打印qDebug()<<"Socket status: connected. Looking for packets...";然后一直停留waitForReadyRead直到准备好读取一些数据。

问题是未检测到断开连接。如果我从操作系统选项断开网络连接,或者即使我拔下以太网线,它的行为也是一样的:套接字状态等于QAbstractSocket::ConnectedState,所以它继续,但当然没有收到任何东西。

我还尝试检测断开连接disconnected()信号(在第一次连接后)到重新连接功能:

// Detect disconnection in order to reconnect
    connect(socket, SIGNAL(disconnected()), this, SLOT(reconnect()));

void MyClass::reconnect(){
    qDebug()<<"Signal DISCONNECTED emitted. Now trying to reconnect";
    panelGUI->mostrarValueOffline();
    socket->close();
    prepareSocket((Global::directionIPSerialServer).toLocal8Bit().data(), 8008, socket);
    qDebug()<<"Reconnected? Status: "<<socket->state();
}

但是信号永远不会发出,因为这段代码永远不会被执行。这是合乎逻辑的,因为它看起来像套接字状态总是ConnectedState.

如果我再次插入,连接将恢复并再次开始接收数据,但我确实想检测断开连接以在 GUI 上显示“断开连接”。

为什么 QTcpSocket 会这样,我该如何解决这个问题?

编辑:我在类构造函数中创建套接字,然后初始化调用 prepareSocket 函数:

socket = new QTcpSocket();
socket->moveToThread(this);

bool prepareSocket(QString address, int port, QTcpSocket *socket) {
    socket->connectToHost(address, port);
    if(!socket->waitForConnected(2000)){
        qDebug()<<"Error creating socket: "<<socket->errorString();
        sleep(1);
        return false;
    }
    return true;
}
4

4 回答 4

22

终于在这个Qt 论坛中找到了解决方案:

如果一段时间内没有数据交换,TCP 将开始发送 keep-alive 段(基本上是确认号设置为当前序列号减一的 ACK 段)。另一个对等方然后用另一个确认回复。如果在一定数量的探测段内未收到此确认,则自动断开连接。小问题是内核在连接空闲 2 小时后开始发送 keep-alive 段!因此,您需要更改此值(如果您的操作系统允许)或在您的协议中实现您自己的保持活动机制(就像许多协议一样,例如 SSH)。Linux 允许您使用 setsockopt 更改它:

int enableKeepAlive = 1;
int fd = socket->socketDescriptor();
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &enableKeepAlive, sizeof(enableKeepAlive));

int maxIdle = 10; /* seconds */
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &maxIdle, sizeof(maxIdle));

int count = 3;  // send up to 3 keepalive packets out, then disconnect if no response
setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &count, sizeof(count));

int interval = 2;   // send a keepalive packet out every 2 seconds (after the 5 second idle period)
setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
于 2012-05-04T11:02:46.737 回答
12

我在使用 QT 客户端应用程序时也遇到了类似的问题。基本上我用定时器、信号和插槽来处理它。当应用程序启动时,它会启动一个 4 秒的 checkConnectionTimer。每 4 秒计时器到期,如果客户端套接字状态 != AbstractSocket::Connected 或 Connecting,它会尝试与 clientSocket->connectToHost 连接

当套接字发出“connected()”信号时,它会启动一个 5 秒的服务器心跳计时器。服务器应每 4 秒向其客户端发送一个 1 字节的心跳消息。当我收到心跳(或由 readyRead() 发出的任何类型的消息)时,我会重新启动心跳计时器。因此,如果心跳计时器超时,我假设连接已关闭并调用clientSocket->disconnectFromHost ();

这对于服务器上所有不同类型的断开连接都非常有效,无论是优雅的还是其他的(拉动电缆)。是的,它需要自定义心跳类型的东西,但归根结底,它是最快、最便携的解决方案。

我并不热衷于在内核中设置 KEEPALIVE 超时。这种方式更便携。在构造函数中:

connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readMessage()));
connect(clientSocket, SIGNAL(connected()), this, SLOT(socketConnected()));
connect(clientSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
connect(heartbeatTimer, SIGNAL(timeout()), this, SLOT(serverTimeout()));
...
// Other Methods

void NetworkClient::checkConnection(){
    if (clientSocket->state() != QAbstractSocket::ConnectedState &&
            clientSocket->state() != QAbstractSocket::ConnectingState){
        connectSocketToHost(clientSocket, hostAddress, port);
    } 
}

void NetworkClient::readMessage()
{
    // Restart the timer by calling start.
    heartbeatTimer->start(5000);
    //Read the data from the socket
    ...
}

void NetworkClient::socketConnected (){
    heartbeatTimer->start(5000);
}

void NetworkClient::socketDisconnected (){
    prioResponseTimer->stop();
}

void NetworkClient::serverTimeout () {
    clientSocket->disconnectFromHost();
}
于 2012-09-13T18:48:33.153 回答
1

试试这个信号槽连接:

connect(this, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onStateChanged(QAbstractSocket::SocketState)));

在插槽实现时:

void TCPWorker::onStateChanged(QAbstractSocket::SocketState socketState ){
qDebug()<< "|GSTCPWorkerThread::onStateChanged|"<<socketState;
...}

我有同样的问题,但是您的问题(始终连接),在拔下以太网线后,我有 4-5 秒的延迟来接收断开信号。

仍在寻找解决方案,如果找到,请发布答案。

于 2013-07-29T11:30:26.157 回答
0

在 Qt 中尝试我的客户端模板:

class Client: public QTcpSocket {
   Q_OBJECT
public:
    Client(const QHostAddress&, int port, QObject* parent= 0);
    ~Client();
    void Client::sendMessage(const QString& );
private slots:
    void readyRead();
    void connected();
public slots:
    void doConnect();
};

在 cpp 上:

void Client::readyRead() {

    // if you need to read the answer of server..
    while (this->canReadLine()) {
    }
}

void Client::doConnect() {
    this->connectToHost(ip_, port_);
    qDebug() << " INFO : " << QDateTime::currentDateTime()
            << " : CONNESSIONE...";
}

void Client::connected() {
    qDebug() << " INFO : " << QDateTime::currentDateTime() << " : CONNESSO a "
            << ip_ << " e PORTA " << port_;
    //do stuff if you need
}


void Client::sendMessage(const QString& message) {
    this->write(message.toUtf8());
    this->write("\n"); //every message ends with a new line
}

我省略了一些代码作为构造函数和插槽连接..试试这个,如果它不起作用,可能服务器端有问题..

于 2012-05-04T09:12:14.870 回答