记一个QSharedPointer 和 QTcpsocket一起使用的断开连接导致段错误问题
写了一个简易的TCPserver,需要很多回调需要socket再回调内写数据,就需要保证回调执行的时候,socket不能被删除,所以采用智能指针老保证QTcpSocket 类的生命周期,加上本地管理,写了如下代码:
- Socket
“` C++
class SCPISocket : public QTcpSocket ,public QEnableSharedFromThis
{
Q_OBJECT
public:
explicit SCPISocket(qintptr sptr,QObject *parent = nullptr);
~SCPISocket();
inline const QString & ID() const {return _id;}
inline const QSharedPointer
device() const {return _device;}
-
void writeCMD(QSharedPointer
& cmd,const std::function & build
= std::bind(buildRet,std::placeholders::_1));
signals:
void onClose(const QString & dev);
private slots:
void onDisconnect();
void init();
void readed();
private:
QString _id;
};
SCPISocket::SCPISocket(qintptr sptr, QObject *parent)
- QTcpSocket{parent} { id = QUuid::createUuid().toString(); setSocketDescriptor(sptr); connect(this,&SCPISocket::disconnected,this,&SCPISocket::onDisconnect); connect(this,&SCPISocket::readyRead,this,&SCPISocket::readed); } SCPISocket::~SCPISocket() { } void SCPISocket::init() { } void SCPISocket::onDisconnect() { emit onClose(_id); } void SCPISocket::writeCMD(QSharedPointer & cmd,const std::function & build) { /* auto this = sharedFromThis(); cmd->handler = [this_,build](const QSharedPointer & scmd){ auto status = scmd->status() SystemCMD::IN_finish; auto result = scmd->result(); QByteArray ret; if(!status){ ret.append(“0 \r\n”); } else { ret = build(result); ret.append(“\r\n”); } auto ptr = this_.data(); QMetaObject::invokeMethod(ptr,this_,ret{ if(this_->state() QTcpSocket::ConnectedState){ this_->write(ret); } },Qt::QueuedConnection); }; _device->write(cmd); */ } void SCPISocket::readed() { }
* Server
``` C++
class SCPISocket;
class RemoteSCPIServer : public QTcpServer
{
Q_OBJECT
public:
RemoteSCPIServer();
signals:
void newConnect(const QSharedPointer<SCPISocket> & socket);
protected:
void incomingConnection(qintptr handle);
};
class SCPIServer : public QObject
{
Q_OBJECT
public:
explicit SCPIServer(QObject *parent = nullptr);
void start(const QString & ip,quint16 port,const QSharedPointer<SystemDevice> & d );
void stop();
signals:
void scpiReq(const QString & cmd);
private slots:
void newConnect(const QSharedPointer<SCPISocket> & socket);
void onClose(const QString & dev);
private slots:
void doStart();
void doStop();
private:
QSharedPointer<SystemDevice> _device;
QHash<QString,QSharedPointer<SCPISocket >> _linkSocket;
QString _ip;
quint16 _port;
RemoteSCPIServer * _service = nullptr;
};
RemoteSCPIServer::RemoteSCPIServer()
{}
void RemoteSCPIServer::incomingConnection(qintptr handle)
{
auto socket = QSharedPointer<SCPISocket>::create(handle);// new RemoteSocket(handle);
emit newConnect(socket);
}
SCPIServer::SCPIServer(QObject *parent)
: QObject{parent}
{
}
void SCPIServer::start(const QString &ip, quint16 port,const QSharedPointer<SystemDevice> & d)
{
_ip = ip;
_port = port;
_device = d;
QTimer::singleShot(20,this,&SCPIServer::doStart);
}
void SCPIServer::stop()
{
QTimer::singleShot(0,this,&SCPIServer::doStop);
}
void SCPIServer::doStart()
{
if(_service) return;
_service = new RemoteSCPIServer();
connect(_service,&RemoteSCPIServer::newConnect,this,&SCPIServer::newConnect);
_service->listen(QHostAddress(_ip),_port);
_service->resumeAccepting();
}
void SCPIServer::doStop()
{
if(!_service){
for(auto it = _linkSocket.begin(); it != _linkSocket.end(); ++it){
it.value()->disconnectFromHost();
}
_linkSocket.clear();
_service->close();
_service->deleteLater();
}
_service = nullptr;
}
void SCPIServer::newConnect(const QSharedPointer<SCPISocket> &socket)
{
socket->setDevice(_device);
_linkSocket.insert(socket->ID(),socket);
connect(socket.data(),&SCPISocket::onClose,this,&SCPIServer::onClose);
}
void SCPIServer::onClose(const QString &dev)
{
_linkSocket.remove(dev);
}
本来这就是一个很简单的智能指针管理的程序,默认Server里保留其计数,如果有lambda再其他线程使用,再lamba的结构体中同时保留,这样,即使连接断开,server中清除其引用,也不会造成作物或者内存泄漏。
但是再实际使用过程中,客户点断开会触发段错误,堆栈信息还是Qt事件循环中的,让人很纳闷。
