最近写一个微型的网络转发,即软件开一个端口等待客户端连接,同时连接到另一个服务器,将客户端的请求转发给服务器,将服务器的返回转发给客户端。因为功能很简单,因此直接采用了控制台方式。
原本以为是很简单的事情,但是在处理客户端的disconnect信号的时候,出了问题。
在客户端disconnect的时候,需要做两件事:
1、delete掉为其创建的QTcpSocket指针_socket;
2、在管理客户端指针的QList lst_sockets中删除掉该节点。
问题来了,因为没有窗体,所以直接使用了lambda方式,为了简便,所以直接[=]了。然而这时候,QList并没有执行removeOne。经过跟踪发现,此时lst_sockets中节点为0。为了正常使用lst_sockets,必须按引用捕获,于是[&]。
问题又来了,这下只要一使用_socket指针,就会出现段错误。按理说,sender和receiver都是const修饰,不应该出现这个问题的,但我就遇到了。
于是想到办法:将lst_sockets的定义移到main函数外,使之成为全局变量,然后按值捕获_socket指针,可以解决。
又想到,何必这么麻烦,直接[_socket,&lst_sockets],解决了。
最终代码为:
QObject::connect(_socket,&QTcpSocket::disconnected,[_socket,&lst_sockets]{
sginfo<<"客户端已断开:"<<_socket->peerAddress();
lst_sockets.removeOne(_socket);
_socket->deleteLater();
});
//QObject::connect(_socket,&QTcpSocket::disconnected,_socket,&QTcpSocket::deleteLater);
至于_socket的delete,是直接在槽函数中调用deleteLater,还是直接将信号和deleteLater槽链接,我认为两者都是一样的。因为deletelater是这样的:
void QObject::deleteLater()
{
QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}
bool QObject::event(QEvent *e)
{
switch (e->type()) {
......
case QEvent::DeferredDelete:
qDeleteInEventHandler(this);
break;
}
}
void qDeleteInEventHandler(QObject *o)
{
delete o;
}
另外,在LInux下,因为开启了端口服务,一定要注意给软件提权,否则每次调试都sudo,麻烦。
又另,QObject::connect需要sender,需要signal,但是不一定要有receiver,可以直接connect到一个全局函数上即可。