Qt之进程间通信(TCP/IP)
简述
可以通过Qt提供的IPC使用TCP/IP,使用QtNetwork模块即可实现,TCP/IP在实现应用程序和进程内部通信或与远程进程间的通信方面非常有用。
QtNetwork模块提供的类能够创建基于TCP/IP的客户端与服务端应用程序。为实现底层的网络访问,可以使用QTcpSocket、QTcpServer和QUdpSocket,并提供底层网络类。还提供了使用常规协议实现网络操作的QNetworkRequest、QNetworkReply、QNetworkAccessManager。
QtNetwork
作为使用IPC的方法,TCP/IP可以使用多种类进行进程内部和外部的通信。
QtNetwork模块提供的类:
类 | 说明 |
---|---|
QLocalServer | 基于服务器的本地套接字的类 |
QLocalSocket | 支持本地套接字的类 |
QNetworkAccessManager | 处理从网络首发收据响应的类 |
QSocketNotifier | 监控从网络通知消息的类 |
QSsl | 在所有网络通信上用于SSL认证的类 |
QSslSocket | 支持通过客户端和服务器端加密的套接字的类 |
QTcpServer | 基于TCP的服务器端类 |
QTcpSocket | TCP套接字 |
QUdpSocket | UDP套接字 |
除表中所示,Qt提供的QtNetwork模块还支持多种协议。如果需要实现内部进程间的通信,建议使用QLocalSocket类。
下面我们来看一个示例,可以在Creator自带的示例中查找QLocalSocket或Local Fortune。
Server
首先,启动Server,这是必然的,服务端不开启,客户端怎么连接得上呢?
server = new QLocalServer(this);
// 告诉服务器监听传入连接的名字。如果服务器当前正在监听,那么将返回false。监听成功返回true,否则为false
if (!server->listen("fortune")) {
QMessageBox::critical(this, tr("Fortune Server"),
tr("Unable to start the server: %1.")
.arg(server->errorString()));
close();
return;
}
fortunes << tr("You've been leading a dog's life. Stay off the furniture.")
<< tr("You've got to think about tomorrow.")
<< tr("You will be surprised by a loud noise.")
<< tr("You will feel hungry again in another hour.")
<< tr("You might have mail.")
<< tr("You cannot kill time without injuring eternity.")
<< tr("Computers are not intelligent. They only think they are.");
// 有新客户端进行连接时,发送数据
connect(server, SIGNAL(newConnection()), this, SLOT(sendFortune()));
// 发送数据
void Server::sendFortune()
{
// 从fortunes中随机取出一段字符串然后进行写入。
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << fortunes.at(qrand() % fortunes.size());
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));
// nextPendingConnection()可以返回下一个挂起的连接作为一个连接的QLocalSocket对象。
QLocalSocket *clientConnection = server->nextPendingConnection();
connect(clientConnection, SIGNAL(disconnected()),
clientConnection, SLOT(deleteLater()));
clientConnection->write(block);
clientConnection->flush();
clientConnection->disconnectFromServer();
}
socket被当做server的孩子创建,这意味着,当QLocalServer对象被销毁时它也会被自动删除。这明显是一个删除对象的好主意,使用完成以后,避免了内存的浪费。
Client
启动客户端,连接到对应的服务器,如果连接不上,则进行错误处理。
socket = new QLocalSocket(this);
connect(getFortuneButton, SIGNAL(clicked()),
this, SLOT(requestNewFortune()));
connect(socket, SIGNAL(readyRead()), this, SLOT(readFortune()));
connect(socket, SIGNAL(error(QLocalSocket::LocalSocketError)),
this, SLOT(displayError(QLocalSocket::LocalSocketError)));
// 连接到服务器,abort()断开当前连接,重置socket。
void Client::requestNewFortune()
{
getFortuneButton->setEnabled(false);
blockSize = 0;
socket->abort();
socket->connectToServer(hostLineEdit->text());
}
// 读取服务器端发送的数据
void Client::readFortune()
{
// 读取接收到的数据
QDataStream in(socket);
in.setVersion(QDataStream::Qt_4_0);
if (blockSize == 0) {
if (socket->bytesAvailable() < (int)sizeof(quint16))
return;
in >> blockSize;
}
if (in.atEnd())
return;
QString nextFortune;
in >> nextFortune;
// 如果当前的数据和收到的数据相同,则重新请求一次,因为是随机的字符串,所以肯定不会每次都相同。
if (nextFortune == currentFortune) {
QTimer::singleShot(0, this, SLOT(requestNewFortune()));
return;
}
currentFortune = nextFortune;
statusLabel->setText(currentFortune);
getFortuneButton->setEnabled(true);
}
// 发生错误时,进行错误处理
void Client::displayError(QLocalSocket::LocalSocketError socketError)
{
switch (socketError) {
case QLocalSocket::ServerNotFoundError:
QMessageBox::information(this, tr("Fortune Client"),
tr("The host was not found. Please check the "
"host name and port settings."));
break;
case QLocalSocket::ConnectionRefusedError:
QMessageBox::information(this, tr("Fortune Client"),
tr("The connection was refused by the peer. "
"Make sure the fortune server is running, "
"and check that the host name and port "
"settings are correct."));
break;
case QLocalSocket::PeerClosedError:
break;
default:
QMessageBox::information(this, tr("Fortune Client"),
tr("The following error occurred: %1.")
.arg(socket->errorString()));
}
getFortuneButton->setEnabled(true);
}