shadowsocks / libQtShadowsocks

A lightweight and ultra-fast shadowsocks library written in C++14 with Qt framework
GNU Lesser General Public License v3.0
1.5k stars 645 forks source link

TcpRelay 可能会过早的发出finished信号, 数据发送完毕之前被释放 #209

Open zhaoyanliang2 opened 5 years ago

zhaoyanliang2 commented 5 years ago

我在 tcprelay.cpp 中发现了以下代码

void TcpRelay::close()
{
    if (stage == DESTROYED) {
        return;
    }

    local->close();
    remote->close();
    stage = DESTROYED;
    emit finished();
}

看下Qt文档对close()的描述

void QAbstractSocket::close() Reimplemented from QIODevice::close(). Closes the I/O device for the socket and calls disconnectFromHost() to close the socket's connection. See QIODevice::close() for a description of the actions that occur when an I/O device is closed.

close()内部调用了disconnectFromHost(), 再看文档对disconnectFromHost()的描述

void QAbstractSocket::disconnectFromHost() Attempts to close the socket. If there is pending data waiting to be written, QAbstractSocket will enter ClosingState and wait until all data has been written. Eventually, it will enter UnconnectedState and emit the disconnected() signal.

调用disconnectFromHost()后QAbstractSocket对象如果还有没发送完的数据, 将会处于ClosingState的状态, 等待数据发送. 最终变成UnconnectedState状态.

举一个实例: 假设local网络速度较慢, remote网络速度较快, remote接收到了大量的数据, 然后remote的远端紧接着立即主动断开了连接, remote会发出error(RemoteHostClosedError)信号, 接下来是这些调用: TcpRelay::onRemoteTcpSocketError() -> TcpRelay::close() -> TcpRelay::finished() TcpRelay::finished()会导致TcpRelay对象被释放, localremote 随其同时释放, 但由于local网络速度较慢, 可能还出于ClosingState的状态, 数据尚未发送完毕就被释放掉了

DaDaQingFeng commented 5 years ago

@zhaoyanliang2 如何解决stop的时候崩溃哇?