进程通信(IPC)的方法有很多,项目开发中,需要根据业务需求来选择适合的IPC方式。所谓LocalSocket,其实就是在socket的基础上衍生出来的一种IPC通信机制。其旨在解决同一台主机上不同进程间互相通信的问题,不能像网络通信使用的socket一样实现不同主机间通信。但正因为这一点,它不需要经过网络协议栈,不需要打包拆包、计算校验,所以执行效率要更高。
而Qt对LocalSocket进行了封装(QLocalSocket),使其用起来更方便,下面介绍QLocalSocket在本地进程间进行通信的简单用法:
服务端:
创建QLocalServer,connect发现新连接的槽函数。
QLocalServer *m_server = new QLocalServer(this);
//myserver为服务端名称,类似于IP+PORT,客户端连接需要与其保持一致
if( m_server->listen("myserver") ) //监听
{
connect(m_server,SIGNAL(newConnection()),this,SLOT(new_connection()));
printf("listen success!!\n");
}
else
printf("listen fail\n");
//m_server->removeServer("myserver"); //如果SERVER已经存在,则需删除
发现新连接槽函数处理:
void Widget::new_connection()
{
qDebug()<<"发现新连接!!";
ui->textBrowser->append("发现新连接!!");
QLocalSocket *newsocket = m_server->nextPendingConnection(); //获取连接上的客户端句柄
connect(newsocket, SIGNAL(readyRead()), this, SLOT(recv_data())); //关联数据接收槽函数
}
由于建立的每个socket连接都关联到了同一个数据接收槽函数上了,槽函数需进行如下处理,来获取对应有数据的socket连接进行数据读取:
void Widget::recv_data()
{
// 取得是哪个localsocket可以读数据了
QLocalSocket *local = static_cast<QLocalSocket *>(sender());
if (!local)
return;
QByteArray rcv_data = local->readAll();
qDebug()<<"rcv_data:"<<rcv_data;
if(is_first_connect(local))
{
local->write(QString("欢迎连接myserver!!").toUtf8());
ui->textBrowser->append(QString("这个socket是首次连接"));
}
else
{
local->write(rcv_data);
ui->textBrowser->append(QString("发送数据:%1").arg(QString(rcv_data)));
}
}
利用 QList来记录socket连接,判断是否为首次连接。(扩展:可以在客户端建立连接后,由客户端向服务端发送特定的数据,服务端首次接收到数据时,将特定数据对应的类型与该socket存进QMap中,建立映射关系。之后通过类型,获取到对应的socket句柄,就能向指定的client发送数据了。)
// QList<QLocalSocket *> local_sockets; 利用Qlist来存储新连接
bool Widget::is_first_connect(QLocalSocket *newsocket) //是否为首次连接
{
int len = local_sockets.length();
for(int i=0; i<len; i++)
{
if(newsocket == (QLocalSocket *)local_sockets.at(i))
return false;
}
local_sockets.append(newsocket);
return true;
}
客户端:
client相对简单,但是考虑到CS模型,如果client先运行,server后运行,则会通信不上,所以需要对客户端增加定时重连处理。
QTimer *reconnect_timer = new QTimer(this);
connect(reconnect_timer,SIGNAL(timeout()),this,SLOT(reconect_to_server()));
m_socket = new QLocalSocket(this);
connect(m_socket,SIGNAL(error(QLocalSocket::LocalSocketError)),this,SLOT(error_proc(QLocalSocket::LocalSocketError))); //数据接收
connect(m_socket,SIGNAL(connected()),this,SLOT(connect_success())); //数据接收
connect(m_socket,SIGNAL(disconnected()),this,SLOT(disconnect_from_server())); //连接断开
connect(m_socket,SIGNAL(readyRead()),this,SLOT(rcv_data())); //数据接收
m_socket->connectToServer("myserver"); //连接到服务器
槽函数实现如下:
void Widget::rcv_data() //收到数据
{
QByteArray data = m_socket->readAll();
ui->textBrowser->append(QString(data));
qDebug()<<"data:"<<data;
}
void Widget::connect_success()
{
ui->textBrowser->append("连接服务端成功!");
}
void Widget::disconnect_from_server()
{
ui->textBrowser->append("连接断开!");
}
void Widget::reconect_to_server()
{
reconnect_timer->stop();
ui->textBrowser->append("连接重试中...");
if(m_socket)
m_socket->connectToServer("myserver");
}
void Widget::error_proc(QLocalSocket::LocalSocketError state)
{
ui->textBrowser->append("连接服务器失败!");
ui->textBrowser->append(QString("错误码:%1").arg(QString::number(state)));
m_socket->close();
reconnect_timer->start(1000); //1s后重连
}
运行结果展示:
先运行服务端,后运行客户端:
先运行客户端,后运行服务端: