最近使用一个第三方PDF控件,此控件提供的一个打开文件方法只能支持http和https协议,所以没办法加载本地文件,最后试了半天各种方案,决定在程序内部启动一个简单的http服务器。
原理很简单利用传输层的TCP协议,模拟HTTP协议。
首先搞清http是如何加载网络文件的,方法很简单,在浏览器里面打开一个文件(我这是需求是PDF类型),然后用抓包工具看返回数据格式:
HTTP/1.1 200 OK
Content-Type: application/pdf
Last-Modified: Mon, 08 Dec 2014 08:58:40 GMT
Accept-Ranges: bytes
ETag: "9516d29c512d01:0"
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Mon, 02 Nov 2015 08:34:34 GMT
Content-Length: 545923
%PDF-1.5
%µµµµ
1 0 obj
<</Type/Catalog/Pages 2 0 R/Lang(zh-CN) /StructTreeRoot 38 0 R/MarkInfo<</Marked true>>>>
endobj
2 0 obj
这是一个简单的http协议,不是所有项都需要,只要模拟下面几项就可以了
1.HTTP/1.1 200 OK
2.Content-Type: application/pdf
3.Accept-Ranges: bytes
4.Content-Length: 545923
5.文件二进制数据
下面是代码:
httpSvr.h
#pragma once
#include <QCoreApplication>
#include <QNetworkInterface>
#include <iostream>
#include <QObject>
#include <QTcpSocket>
#include <QTcpServer>
#include <QDebug>
#define PORT 61301 // TCP 端口1
class httpSvr : public QObject
{
Q_OBJECT
public:
explicit httpSvr(QObject *parent = 0);
~httpSvr();
QTcpSocket *socket;
int port();
public slots:
void myConnection();
void readMessage();
private:
QTcpServer *server;
int _port;
signals:
};
httpSvr.cpp
#include "httpSvr.h"
#include <QFile>
#include <QChar>
using namespace std;
httpSvr::httpSvr(QObject *parent) : QObject(parent)
{
socket = 0; // 客户端socket
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()),this, SLOT(myConnection()));
qDebug()<< "httpSvr start listen, port:"<< PORT;
if(server->listen(QHostAddress::Any, PORT)) {
qDebug()<<"success";
} else {
qDebug()<<"failed";
}
}
int httpSvr::port() {
return PORT;
}
void httpSvr::readMessage(){
QString readmsg = socket->readAll();
//GET /J%3A%2Fmyclient%2Fmyclient%2FZB%2F2015_10_24_20_18_44%2FGC%2FZBWJ.pdf HTTP/1.1
// 通过URL取文件路径,具体看下面说明
QStringList msgList = readmsg.split('n');
QString getURL = msgList.first().toUpper();
getURL.replace("GET /", "");
getURL.replace(" HTTP/1.1", "");
getURL.replace("r", "").replace("n", "");
getURL.trimmed();
//URL解码
QByteArray ba;
QString getPath = ba.fromPercentEncoding(getURL.toUtf8());
QFile f(getPath);
if(!f.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) {
socket->disconnectFromHost();
return;
}
QByteArray barr = f.readAll();
f.close();
// 模拟http协议
QString lens(QString::number( barr.length()));
socket->write("HTTP/1.1 200 OKrn");
socket->write("Content-Type: application/pdfrn");
socket->write("Accept-Ranges: bytesrn");
QString sLen("Content-Length: "+lens+"rnrn");
socket->write(sLen.toLatin1());
socket->write(barr);
socket->flush();
connect(socket, SIGNAL(disconnected()),socket, SLOT(deleteLater()));
socket->disconnectFromHost();
}
void httpSvr::myConnection()
{
socket = server->nextPendingConnection();
if (socket) {
connect(socket, SIGNAL(disconnected()),socket, SLOT(deleteLater()));
connect(socket,SIGNAL(readyRead()),this,SLOT(readMessage())); //有可读的信息,触发读函数槽
}
}
httpSvr::~httpSvr()
{
if (socket)
socket->close();
}
上面主要看readMessage方法,流程是解析http请求,获取要加载的文件路径,然后读取文件二进制数据,模拟http数据报文,通过socket发出即可。
http服务如何知道读取哪个文件呢?这地方我是通过URL来传递的,将带加载的文件的全路径,通过URL编码后拼接在URL请求后面,httpsvr解析请求,通过解码获取文件路径,再加载即可。
启动服务:
httpSvr *server; //全局的
server = new httpSvr();
调用方法:
QString path = "d:/myclient/myclient/abc.pdf";
QString baPath = path.toUtf8().toPercentEncoding();
QString openUrl = QString("http://127.0.0.1:61301/%1").arg(baPath);
myCtrl.OpenUrl(openUrl);// 我的第三方控件调用方法
因为是本机,所以http的地址是127.0.0.1, 后面端口号是服务里面定义的端口号。然后将待加载的文件路径编码后拼接在地址后面。
也可以直接在浏览器里面打开openUrl地址,直接看到效果。
转载于:https://my.oschina.net/u/2492458/blog/524946
最后
以上就是认真香氛最近收集整理的关于QT下用代码在程序内部搭建一个简单的HTTP协议服务器的全部内容,更多相关QT下用代码在程序内部搭建一个简单内容请搜索靠谱客的其他文章。
发表评论 取消回复