0

我为接受来自不同客户端的连接的服务器编写了代码。每个客户端都在不同的线程中服务。每个线程访问数据库以获取数据,然后将此数据更新到连接到服务器的所有客户端。

1)当用户界面第一次向服务器请求数据时,它会正确响应,但在那之后服务器没有读取套接字,即服务器的 readyread() 没有被调用。有趣的是,这在mac 和 linux中运行良好,此问题仅在windows上可见

2)我能够验证当 DB 模块发出一个被线程捕获的信号时,会发生挂起,因为当我移除发射时一切正常。

在这里,我附上了所有需要的 .h 和 .cpp 代码

定义文件

#ifndef DEFN_H
#define DEFN_H

struct PresetData{
    QString ID;
    QString name;
    QString value;
    QString source;
}; 

#endif // DEFN_H

主文件

#include <QCoreApplication>
#include "myserver.h"
#include "mydb.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyDB db;
    MyServer server(&db);
    server.startServer();

    return a.exec();
}

数据库文件

#ifndef MYDB_H
#define MYDB_H

#include <QObject>
#include <QtSql>

#include "Defn.h"

class MyDB : public QObject
{
    Q_OBJECT
  public:
    explicit MyDB(QObject *parent = 0);

  signals:
    void dataAvailable(QString ID, QString name, QString value, QString source);

  public slots:
    void onUpdateData(QString ID, QString name, QString value, QString source);
    void onGetData(QString ID, QString name, QString value, QString source);

  private:
    QSqlDatabase m_db;
};

#endif // MYDB_H

我的数据库

#include "mydb.h"

MyDB::MyDB(QObject *parent) :
QObject(parent)
{
    m_db = QSqlDatabase::addDatabase("QSQLITE");
    m_db.setConnectOptions();
    m_db.setDatabaseName("D:/MySimulator/New Folder/TCPServer1/DB.db");

    if (m_db.open()){
       qDebug() << "DB opened succesfully" ;
    }else{
       qDebug() << "DB Opening failed" ;
    }

    QStringList tables = m_db.tables();
    if (tables.contains("Presets", Qt::CaseInsensitive)){
         qDebug() << "DB Contains Data" ;
         return;
    }
}

void MyDB::onGetData(QString ID, QString name, QString value, QString source)
{
    qDebug() << "onGetData" ;
    QString queryString = "SELECT Value from 'Presets' where ID = \'" + ID + "\'";
    QSqlQuery q;

    bool result = q.exec(queryString);

    if (result){
       if (q.next()){
         value = q.value(q.record().indexOf("Value")).toString();
         qDebug() << " Retrieved Value = " << value ;
         emit dataAvailable(ID, name, value, source);
       }else{
         qDebug("Empty Result");
       }
   }else{
      qDebug("NO Result");
   }
}

void MyDB::onUpdateData(QString ID, QString name, QString value, QString source)
{
    qDebug() << "onUpdateData" ;
    QString queryString = "UPDATE 'Presets' SET Value = \'" + value + "'\ WHERE ID = \'" + ID + "\'";
    QSqlQuery q;

    QSqlDatabase::database().transaction();
    bool result = q.exec(queryString);

    if (result){
       QSqlDatabase::database().commit();
       onGetData(ID, name, "", "000");
    }else{
        qDebug("NO Result");
    }
}

我的线程.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QDebug>
#include "Defn.h"
#include "mydb.h"

class MyThread : public QThread
{
     Q_OBJECT
   public:
     explicit MyThread(int ID, MyDB* db, QObject * parent = 0);

     void run();
     void parseInput(QString string);

   signals:
     void error(QTcpSocket::SocketError socketError);
     void updateData(QString ID, QString name, QString value, QString source);
     void getData(QString ID, QString name, QString value, QString source);

   public slots:
     void readyRead();
     void disconnected();
     void onDataAvailable(QString ID, QString name, QString value, QString source);

   private:
     QTcpSocket* socket;
     int socketDescriptor;
     MyDB* db;
};

#endif // MYTHREAD_H

我的线程.cpp

#include "mythread.h"
#include "qtcpserver.h"
#include "qabstractsocket.h"

MyThread::MyThread(int ID, MyDB* db, QObject * parent ):
   QThread(parent)
{
    this->socketDescriptor = ID ;
    this->db = db;
}

void MyThread::run()
{
    // thread starts here.
    qDebug() << socketDescriptor << "Starting Thread" ;
    socket = new QTcpSocket();
    if (!socket->setSocketDescriptor(this->socketDescriptor)){
        emit error(socket->error());
        return;
    }

    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::DirectConnection);
    connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::DirectConnection);
    connect(this, SIGNAL(getData(QString, QString , QString , QString )), this->db, SLOT(onGetData(QString , QString , QString , QString )));
    connect(this, SIGNAL(updateData(QString , QString , QString , QString )), this->db, SLOT(onUpdateData(QString , QString , QString , QString )));
    connect(this->db, SIGNAL(dataAvailable(QString , QString , QString , QString )), this, SLOT(onDataAvailable(QString , QString , QString , QString )));

    qDebug() << socketDescriptor << "Client Connected" ;

    exec();
}

void MyThread::readyRead()
{
    QByteArray data = socket->readAll();
    qDebug() << socketDescriptor << "Data in: " << data;
    parseInput(data);
}

void MyThread::disconnected()
{
    qDebug() << socketDescriptor << "Disconnected" ;
    socket->deleteLater();
    exit(0);
}

void MyThread::parseInput(QString dataFromTCP)
{
    qDebug() << socketDescriptor << ":" <<"parseInput  "  << dataFromTCP;

    if (dataFromTCP.isEmpty())
       return;

    QStringList list1 = dataFromTCP.split("\n", QString::SkipEmptyParts);

    qDebug() << socketDescriptor << ":" << "list1 BEGIN";
    for (int i = 0 ; i < list1.count(); i++)
    {
        qDebug() << i<< ":" << list1.at(i);
    }
    qDebug() << socketDescriptor << ":" << "list1 END";

    if (list1.count() < 1){
         return;
    }

    QString strMessage = "";
    for (int i = 0 ; i < list1.count() ; i++)
    {
        strMessage = list1[i];
        QStringList list2 = strMessage.split(" ", QString::SkipEmptyParts);

        qDebug() << socketDescriptor << ":" << "list2 BEGIN";
        for (int i = 0 ; i < list2.count(); i++)
        {
            qDebug() << i<< ":" << list2.at(i);
        } 
        qDebug() << socketDescriptor << ":" << "list2 END";

        if (list2.count() < 1){
            break;
        }

        QString ID = list2[1];
        QString source = QString::number(socketDescriptor) ;
        if (list2[0] == "GET"){
           emit getData(ID, "", "", source);
        } 
        else if (list2[0] == "UPD"){
            QString value = list2[2];
            emit updateData(ID, "", value, source);
        }
    }
}

void MyThread::onDataAvailable(QString ID, QString name, QString value, QString source)
{
    if( (QString::number(socketDescriptor) == source) || ("000" == source ) ) {
       qDebug() << socketDescriptor << " : On Data Available " << ID << name << value ;
       QString data = "DATA " + ID + " " + value + " " + "\n" ;
       QByteArray ba;
       ba.append(data);
       socket->write(ba);
    }
}

我的服务器.h

#ifndef MYSERVER_H
#define MYSERVER_H

#include <QDebug>
#include <QObject>
#include <QTCPServer>
#include <QTCPSocket>

#include "mythread.h"
#include "mydb.h"

class MyServer: public QTcpServer
{
      Q_OBJECT
   public:
      explicit MyServer(MyDB* pdb, QObject* parent = 0);
      void startServer();

   signals:

   public slots:

   protected:
      void incomingConnection(qintptr socketDescriptor);

   private:
      MyDB* pdb ;
};

#endif // MYSERVER_H

我的服务器.cpp

#include "myserver.h"

MyServer::MyServer(MyDB* pdb, QObject* parent ):
   QTcpServer(parent)
{
    this->pdb = pdb;
}

void MyServer::startServer()
{
    if (!this->listen(QHostAddress::Any, 1234)){
        qDebug() << "Could not Start Server " << this->errorString();
    }
    else{
        qDebug() << " Server Running... ";
    }
}

void MyServer::incomingConnection(qintptr socketDescriptor)
{
    qDebug() << socketDescriptor << " Connecting... ";
    MyThread *thread = new MyThread(socketDescriptor, pdb, this);

    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

    thread->start();
}

这里我上面提到的信号是来自“mydb.cpp”的dataAvailable。如果我注释掉该行,则服务器会响应客户端消息。但是,如果在初始响应之后发出该信号,则服务器似乎挂起并且不再对来自客户端的传入消息做出反应。相同的代码在 mac 和 linux 中运行良好。但它仅在 Windows 中存在此问题。有人可以让我知道我做错了什么,它只在 Windows 中失败了吗?提前感谢您帮助我。

编辑:

这段代码的目的是,每当一个线程导致对数据库的更新调用时,每个线程(包括调用更新的线程)都会收到有关更改的通知。因此,预计当时运行的其他线程也会收到信号。

这是对服务器的期望:

  1. 能够同时允许来自多个客户端的 TCP 连接。

  2. 如果任何客户端请求信息,它会通过 TCP 连接获取所需的数据。

  3. 如果任何客户端更新信息,包括更新客户端在内的所有客户端都会通过 TCP 连接获得通知。

4

1 回答 1

0

好吧,对于初学者来说,您的代码完全不是线程安全的。您在 main() 函数中创建 MyDB 的单个实例,然后从线程调用它而不保护其数据成员。此外,会发出信号,在没有任何保护的情况下更新数据。如果两个线程碰巧同时运行怎么办?

其次,这更重要:每当您发出 dataAvailable() 时,您都会在自己的线程中调用其他线程对象中的函数。这是数据到达时的代码路径:

  1. MyThread::parseInput() 发出
  2. MyThread::getData(),连接到
  3. MyDB::onGetData(),它发出
  4. MyDb::dataAvailable,连接到 ( drumroll ....)
  5. MyThread::onDataAvailable,最终调用
  6. 套接字->写()

因此,如果数据到达线程#1,您将从......线程#1 的 MyThread 对象#2、#3、#4 等发送数据。根据操作系统,这是个坏消息。我对 Windows 线程知之甚少,但我知道这段代码最终被破坏了。

如果您只想更新数据库并中继数据,则可以省去线程并使用顺序程序来处理使用常规 Qt 信号和插槽的套接字就好了。

于 2014-08-19T01:14:45.920 回答