QTcpSocket以及connect的一些问题

最近写一个微型的网络转发,即软件开一个端口等待客户端连接,同时连接到另一个服务器,将客户端的请求转发给服务器,将服务器的返回转发给客户端。因为功能很简单,因此直接采用了控制台方式。

原本以为是很简单的事情,但是在处理客户端的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到一个全局函数上即可。

关于在QTableView中的数据更新触发

现有一个要求,即在QTableView中实现类似一些数据库软件表格的功能,即当前编辑行切换时,以及编辑结束后,鼠标离开表格时,数据自动保存且提交。

如果只是第一个要求,那么设置对应model的EditStrategy为OnRowChange即可满足需求,当时这个无法做到鼠标点击其它地方时也提交,要做到这点,好多种方法,最后选取了一种,即响应model的dataChanged信号,在这一个入口中实现两个功能。

代码如下:

void MainWindow::do_view_datachanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles)
{
//如果离开了当前编辑,则提交
if(!ui->tableView->hasFocus())
{
sginfo<<“submit for lost focus”;
last_edit_row = -1;
}
else if(topLeft.row() != last_edit_row){//换行编辑,需要保存最近一次编辑的行,动态更新,同时失去焦点时要设为-1
last_edit_row = topLeft.row();
sginfo<<“submit for editline changed”;
}
}

从mysql转存sqlite

由于不能使用工具,只能编码,目前也没有想到什么好的办法,于是最常规的办法:从mysql读取,写入到sqlite中去。

前提是,mysql和sqlite的表结构必须一致。

为了加快速度,采用了内存写入的方法。即服务器端保持一个空的sqlite数据,需要的时候,将空sqlite数据库读入内存,建立一个内存数据库。

然后读取服务器数据库,对每条数据库,query的记录生成sql的insert语句。由于sqlite的特殊行,即sqlite的字段可以存储任何类型的数据,因此可以将服务器的记录统一转换为string类型,然后统一进行插入。当然,开启事务。

转移完成后,将内存数据库写入到硬盘。

要记得,内存数据库在写入到硬盘后,要close掉,然后remove掉,否则复制的内存数据库内存会造成浪费,而且多次后,会内存耗尽。

经过测试,22.7m的sqlite数据库,总记录条数在13万条左右(每张表大约30个字段),写入数据库大约10秒左右,整体完成控制在20秒之内。