[Qt]Tcp Server模拟Http Server实现Web实时监控(画面+数据)

实现这个功能我们需要继承重写两个类,一个是线程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>进行整体替换,把最初的布局代码重新发一遍,就可以实现清空现有的网页代码。

由于实现过程比较复杂,所以我描述得也有点乱,如果你想实现,感觉还是需要不断测试。希望对你们有用~

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值