недели три назад я с такой проблемой столкнулся.
сделал по простому
добавил в БД таблицу в которой регистрируется каждое клиентское приложение
при подключении к БД. ( оно там оставляет свой ip, порт и т.д.).
при изменении к.либо данных каждый клиент оповещает других через TCP - сокеты
(о существовании других он узнает из БД).
т.е. отправил запрос на модификацию данных, отправь уведомление по сети другим.
для этого создал специальный обьект Master который принимает сигналы о том что ч.либо изменилось
и оповещает все обьекты внутри приложения а также другие клиентские приложения.
реализовал пока топорно, еще буду переделывать, вот исходники :
.h
class Master : public QObject
{
// Обьект передающий уведомление о том что к.либо таблица изменилась
Q_OBJECT
public :
static Master * getObject();
public slots :
void tableChange(const QString & );
// этот слот связан со всеми виджетами с помощью которых пользователь может менять данные
// он будет вызыватся каждый раз когда к.либо обьект проги изменил данные.
void beforeQuit();
void acceptConnection();
signals :
void RefreshTable(const QString & );
// сигнал рассылается всем виджетам
// в качестве параметра передается имя таблицы которая была изменена.
private :
Master(QObject * parent = 0);
static Master * pointer;
sendThread * sThread;
QTcpServer * tcpServer;
quint16 port;
QHostAddress hAdr;
};
class sendThread : public QThread
{
// Поток в котором будем рассылать уведомления другим клиентским прогам.
Q_OBJECT
public :
sendThread(QObject * parent = 0);
void start(const QString &);
void setAddress(QHostAddress & ,quint16 port);
protected:
void run();
private :
int q;
QString tName;
QSqlDatabase db;
QHostAddress hAdr;
quint16 port;
};
.cpp
Master * Master::pointer = 0;
Master::Master(QObject * parent)
:QObject(parent)
{
QSqlDatabase db = G::getDB();
hAdr = QHostInfo::fromName(QHostInfo::localHostName()).addresses().last();
// создаем сервер
tcpServer = new QTcpServer(this);
tcpServer->listen(hAdr);
port = tcpServer->serverPort();
// ДОБАВЛЯЕМ СЕБЯ В ip_table .
QSqlQuery query(G::getDB());
while(true)
{
if(!query.exec("SELECT max(`key`) FROM ip_table;"))
{
QMessageBox::information(0,"111",query.lastError().text(),QMessageBox::Yes);
};
query.next();
int key = query.value(0).toInt();
key++;
QString insert = "INSERT INTO ip_table VALUES("+QString::number(key)
+",'"+hAdr.toString()+"',"+QString::number(port)+",0);";
if(query.exec(insert)) break;
};
// Создаем sendThread
sThread = new sendThread(this);
sThread->setAddress(hAdr,port);
QObject::connect(tcpServer,SIGNAL(newConnection()),this, SLOT(acceptConnection()));
QObject::connect(QApplication::instance(),SIGNAL(aboutToQuit()),this,SLOT(beforeQuit()));
};
void Master::beforeQuit()// Слот связан с QCoreApplication::aboutToQuit () [signal]
{// Удаляем из ip_table данные о себе.
QString q("DELETE FROM ip_table WHERE ip='"+hAdr.toString()+"' AND port="+QString::number(port)+";");
QSqlQuery query(G::getDB());
if(!query.exec(q))
{
QMessageBox::information(0,QString("Удаление из ip_table не произошло.")
,query.lastError().text()+"\n"+q,QMessageBox::Yes);
};
// Если данные о себе не удалим перед выходом то ничего страшного они будут удалены другой клиентской программой. ( потом).
};
void Master::acceptConnection()
{
QTcpSocket * incomeConnection = tcpServer->nextPendingConnection();
QString str;
QDataStream in(incomeConnection);
in.setVersion(QDataStream::Qt_4_0);
while (incomeConnection->bytesAvailable() < (int)sizeof(quint16))
{
if (!incomeConnection->waitForReadyRead(6660))
{
return;
};
};
quint16 size;
in>>size;
while(incomeConnection->bytesAvailable()<size)
{
if(!incomeConnection->waitForReadyRead(6660))
{
return;
};
};
in>>str;
emit RefreshTable(str);
};
void Master::tableChange(const QString & tName)
{
emit RefreshTable(tName); // все виджеты в данном экземпляре проги обновятся.
// и еще рассылаем всем другим прогам сообщение.
sThread->start(tName);
//G::debug("tableChange -"+tName);
};
Master * Master::getObject()
{
if(!pointer)
{
pointer = new Master(QApplication::instance());
};
return pointer;
};
sendThread::sendThread(QObject * parent)
:QThread(parent)
,port(0)
{
db=G::getDB();
};
void sendThread::setAddress(QHostAddress & adr,quint16 q)
{
hAdr = adr;
port = q;
};
void sendThread::start(const QString & tn)
{
if(hAdr.isNull())
{
QMessageBox::information(0,"Alarm",QString::fromLocal8Bit("Не инициализировали"),QMessageBox::Ok);
return;
};
if(!port)
{
return ;
};
tName=tn;
QThread::start();
};
void sendThread::run()
{
QString q("SELECT `key`,`ip`,`port`,`error_count` FROM ip_table WHERE NOT (ip='"+hAdr.toString()+"' AND port="+QString::number(port)+");");
QSqlQuery query(db);
if(!query.exec(q))// выбираем из БД адреса и порты других приложений.
{
QMessageBox::information(0,"QueryError",query.lastError().text(),QMessageBox::Yes);
return;
};
QTcpSocket * tcpSocket = new QTcpSocket(this);
while(query.next())
{
int temp_key = query.value(0).toInt();
QString temp_ip = query.value(1).toString();
int temp_port = query.value(2).toInt();
tcpSocket->connectToHost(QHostAddress(temp_ip),temp_port,QIODevice::WriteOnly);
if(!tcpSocket->waitForConnected(666))
{
QSqlQuery del_q(db); // Если не дозвонились до к.либо клиента то увеличиваем счетчик ошибок иесли их больше трех удаляем из БД.
QString update("UPDATE ip_table SET error_count=error_count+1 WHERE ip= '"
+temp_ip+"' AND port="+QString::number(temp_port)+";");
if(!del_q.exec(update))
{
QMessageBox::information(0,"",del_q.lastError().text(),QMessageBox::Yes);
};
//QMessageBox::information(0,"QueryError","I am in (!tcpSocket->waitForConnected())",QMessageBox::Yes);
QString del("DELETE FROM ip_table WHERE error_count>3");
del_q.exec(del);
}
else
{
//отправляем имя таблицы.
QByteArray block;
QDataStream out(&block,QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << tName;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));
tcpSocket->write(block);
tcpSocket->disconnectFromHost();
};
tcpSocket->waitForDisconnected(666);
};
};
чтобы использовать такой механизм
каждый виджет работающий с БД должен содержать
сигнал для оповещения Master'а о том что таблица с таким-то именем была изменена
и слот принимающий имя таблицы которую надо переSELECT'ить
public slots:
void slt_refresh(const QString&);
signals :
void iChangeTable(const QString & );
void eMAbstractGrid::slt_refresh(const QString& tName)
{
if(tName!=this->tName) return;
setQuery(strQuery);
};