实现这个功能我们需要继承重写两个类,一个是线程QThread,一个是Tcp Server,线程是为了把数据通信和主线程分开,避免阻塞,Tcp Server就不用说了,用来应答浏览器及数据通讯。
我们可以先看头文件,两个类:
class HttpSendThread : public QThread
{
Q_OBJECT
public:
HttpSendThread(QObject *parent = nullptr):QThread(parent)
{
}
~HttpSendThread(){}
void SetParameters(qintptr socketDescriptor);
void run();
qintptr m_socketDescriptor;
protected slots:
void sockdisconnect();
private:
bool m_isRunning;
QTcpSocket *m_socket = nullptr;
};
class MyTcpServer : public QTcpServer
{
Q_OBJECT
public:
MyTcpServer (QObject *parent = 0);
~MyTcpServer ();
qintptr m_socketDescriptor;
bool status();
protected:
void incomingConnection(qintptr socketDescriptor) ;
private:
HttpSendThread *m_httpsendthread = nullptr;
};
在这里介绍一下两个函数:
MyTcpServer中的void incomingConnection(qintptr socketDescriptor) :
这个函数是当tcpserver被连接时触发的槽函数,重写这个函数主要是为了获得socketDescriptor这个描述符
HttpSendThread中的void SetParameters(qintptr socketDescriptor):
这个函数就是用来传递socketDescriptor描述符,使得线程中能够使用socket进行和浏览器通讯,具体可以看下面的实现过程。
cpp文件:
首先是MyTcpServer的函数实现(构造函数可忽略):
//当浏览器通过输入IP:PORT进入时,该函数被调用
void MyTcpServer::incomingConnection(qintptr socketDescriptor)
{
qDebug() << "[OutputPackage]Browser request connection";
if (!m_httpsendthread )
{
m_httpsendthread = new HttpSendThread();
}
if(!m_httpsendthread->isRunning())
{
m_socketDescriptor = socketDescriptor;
//传递描述符
m_httpsendthread->SetParameters(m_socketDescriptor);
m_httpsendthread->start();
}
}
然后是 HttpSendThread类:
void HttpSendThread::SetParameters(qintptr socketDescriptor)
{
m_socketDescriptor = socketDescriptor;
}
void HttpSendThread::run()
{
if(m_socket== nullptr)
{
qDebug() << "Socket init.";
m_socket= new QTcpSocket;
if (!m_socket->setSocketDescriptor(m_socketDescriptor)) {
return;
}
connect(m_socket,SIGNAL(disconnected()),this,SLOT(sockdisconnect()));
}
//到这里,这个线程的run函数就可以正常使用这个m_socket去通信啦
//do something...
}
在run函数中,我们就可以按照http的协议给浏览器返回应答信息啦。
例如:
//这里是http的协议应答头
QString http = "HTTP/1.1 200 OK";
http += QString("Content-Type: text/html; charset=utf-8\r\n\r\n");
//这里是一些网页布局html5+javascript
QString httphtml = QString("<head >");
httphtml += QString("<title >luguosheng0110</title>");
httphtml += QString("<p id='time' align='right'>time</p>");
httphtml += QString("<center>");
httphtml += QString("<h1> LUGUOSHENG</h1>");
httphtml += QString("</center>");
httphtml += QString("<style type='text/css'>");
httphtml += QString(".image{");
httphtml += QString("float:left;width:50%;margin-top:5%;margin-left:2%;}");
httphtml += QString(".text{");
httphtml += QString("border: solid #87CEFA;");
httphtml += QString("height: 570;");
httphtml += QString("border-width: 1px;");
httphtml += QString("background: #F0FFFF;");
httphtml += QString("float:right;width:45%;margin-top:5%;}");
httphtml += QString("</style>");
httphtml += QString("</head>");
httphtml += QString("<div class='text'>");
httphtml += QString("<center>");
httphtml += QString("<h3><p id='t1'>The content you selected will be displayed</p></h3>");
httphtml += QString("</center>");
httphtml += QString("</div>");
httphtml += QString("<div id = 'div1' class = 'image' style = 'width:48% ;'>");
//重点留意一下这里,下面会继续讲
httphtml += QString("<img id = 'p1' src = 'data:image/jpg;base64,%1' width = '100%' alt = 'After the camera captures, the screen will be displayed (or the current device is occupied)' />").arg(qhexed);
httphtml += QString("</div> ");
//合并一起发给浏览器
http += httphtml;
m_socket->write(http.toUtf8());
那么发过去之后的效果是怎样的呢?看下面:
由于我没有加载图片数据,所以图片那里不能显示(在这里感谢菜鸟教程提供平台给我测试验证!)。
那么图片怎么显示呢?
这里介绍一下如何把QImage转换到某个数据类型,然后放在html5代码中一起发送到浏览器显示。
httphtml += QString("<img id = 'p1' src = 'data:image/jpg;base64,%1' width = '100%' alt = 'After the camera captures, the screen will be displayed (or the current device is occupied)' />").
arg(qhexed);
从这段代码可以看到,我是把图片数据放到qhexed中,然后放到QString中。
转换过程:
QImage image;
//把图片数据放到image中,该步省略。
QByteArray ba;
QBuffer buf(&ba);
buf.open(QIODevice::WriteOnly);
if (!image.save(&buf, "jpg", 6))
{
qWarning() << "Jpg image save to buf failed,imageformats folder is missing.";
}
QByteArray hexed = ba.toBase64();
QString qhexed(hexed);
经过这段转换,QImage数据就转成了浏览器可以解析的base64格式了,放在qhexed中。
到这里,其实就可以在浏览器看到图像了。
然后,当你再次使用socket给浏览器发送html代码数据时,你会发现新数据会追加在下面,而不是在原来的位置刷新。这里就需要用到javascript+html5代码进行替换,比如:
//文本更新:
QString txtdata = "<script>'use strict';";
txtdata += QString("function t1myTimer()");
txtdata += QString("{");
txtdata += QString("document.getElementById('time').innerHTML=Date();");
txtdata += QString("document.getElementById('t1').innerHTML = null;");
QString str_send = "new txt";
txtdata += QString("document.getElementById('t1').innerHTML = decodeURIComponent(escape('%1'));").arg(str_send);
txtdata += QString("}");
txtdata += QString("t1myTimer();");
txtdata += "</script>";
//图像更新:
QString imagedata= QString("<script>'use strict';");
imagedata+= QString("function p1myTimer()");
imagedata+= QString("{");
imagedata+= QString("let element = document.getElementById('div1');");
imagedata+= QString("let child = document.getElementById('p1');child.src = null;");
imagedata+= QString("element.removeChild(child);");
imagedata+= QString("let para = document.createElement('img');");
imagedata+= QString("para.setAttribute('id','p1');para.setAttribute('width','100%');");
imagedata+= QString("para.setAttribute('src','data:image/jpg;base64,%1');").arg(qhexed);
imagedata+= QString("let element1 = document.getElementById('div1');");
imagedata+= QString("element1.appendChild(para);");
imagedata+= QString("}");
imagedata+= QString("p1myTimer();");
imagedata+= "</script>";
这样,新数据就会在同样的位置去刷新显示了。
然后,当你在浏览器按F12时,你就会发现html5代码会一直累加一直累加。
这里就需要用到<script>document.body.innerHTML = ' **** ';</script>进行整体替换,把最初的布局代码重新发一遍,就可以实现清空现有的网页代码。
由于实现过程比较复杂,所以我描述得也有点乱,如果你想实现,感觉还是需要不断测试。希望对你们有用~