Qt 框架 开发HTTP 服务器 开发记录
最近需求需要开发一款 HTTP ,然后由于先前接触过Qt,就直接用Qt写HTTP服务器了,也是为了当作练手,要不然是直接上HTTP框架的。
后端用C++ Qt框架 前端为了练手 当然是纯生的 js html css
具体的HTTP 实现过程我就不累赘描述了,这个Http协议解析基本上大部分人都知道原理。
主要是记录一下开发中遇到的各种问题。
首先最开始开发的时候,一路顺风,我的设计模式是 层次 设计模式,一层层独立互不相干互不干涉。严格的只管理好自己的所在层。
数据包是一层层往上传输,到达 Logic 层 指令处理完毕之后 返回要显示的数据(比如HTML),于是再一层层往下返回,一层层加报头;
是不是有点类似于 七层协议?
由于软件本身只是后台界面使用,所以并没有考虑到 线程池,直接使用多线程。
在制作过程中,最经常遇到的莫过于就是编码问题,本身应该是一个很简单的问题,但是有时候确实出现的次数比较多。虽然说解决也简单。
首先我们统一编码。内部程序和源代码和html文件均为 UTF-8。
在开发到 60%,也就是在设计 身份识别的地方,我们想了一个办法,为了保证其安全性,我们用了一直理论上我们没有找到什么缺陷的方法:
根据一段时间的讨论,在不考虑Cookie被盗取(Cookie是以会话的形式存在)的情况下,似乎没有发现什么问题。
以下为 Cookie 生成算法:
QString Cookie::getRandCookie(QString & name, QString & pass){
QByteArray bb;
QString temp;
QString md5_pass;
QString randcookie;
bb = QCryptographicHash::hash(name.toUtf8(), QCryptographicHash::Md5);
temp = bb.toHex();
bb = QCryptographicHash::hash(temp.toUtf8(), QCryptographicHash::Md5);
randcookie = bb.toHex();
bb = QCryptographicHash::hash(pass.toUtf8(), QCryptographicHash::Md5);
temp = bb.toHex();
bb = QCryptographicHash::hash(temp.toUtf8(), QCryptographicHash::Md5);
md5_pass = bb.toHex();
QTime t;
t = QTime::currentTime();
qsrand(t.msec() + t.second() * / );
int n = qrand();
QString tmp = QString::number(n, );
bb = QCryptographicHash::hash(tmp.toUtf8(), QCryptographicHash::Md5);
tmp = bb.toHex();
randcookie = randcookie + tmp + md5_pass;
//目前总共有 32 位,为了防止用户Cookie被XSS以免 碰撞机碰撞。 所以将返回不完整的Md5
// 6~28位是 用户名 33位~64位是随机数无用
QString userhead = randcookie.mid(, ); //取用户名前面这一段 不完整
QString rand = randcookie.mid(,); //随机数
QString passhead = randcookie.mid(, ); //这里去掉前8位 //取中间这一段 不给完整的 MD5码
randcookie = rand+ userhead + passhead;//随机数 账号 密码
randcookie += "_Hi_hacker"; //向大牛打声招呼
return randcookie;
}
如果要是再看的你发现了漏洞,一定要留言告诉我,我将立即改进。
随后我们遇到了编码问题。
一个问题就是,因为我们的需求包括了 网页操作控制台(用匿名管道实现,详情可以看我的这篇文章:http://www.cnblogs.com/suwings/p/5754943.html,顺带一提,Qt框架也可以实现,只是由于时间问题,没法再做描述)
但是Windows 控制台默认是 GBK 编码,这将导致一个问题的出现,中文输入的数据可能将乱码。输出的数据也可能将乱码。
不过在我们测试的发现居然忘记URL中文解码了,但是Qt自带的解码有个问题就是 英文有时候也会一起解码。
于是在网上找到了如下实现方法:
/************************************************************************/
/* URL解码 英文可不解 */
/************************************************************************/
std::string urlDecode(const std::string & _szToDecode)
{
std::string result;
int hex = ;
for (size_t i = ; i < _szToDecode.length(); ++i)
{
switch (_szToDecode[i])
{
case '+':
result += ' ';
break;
case '%':
if (isxdigit(_szToDecode[i + ]) && isxdigit(_szToDecode[i + ]))
{
std::string hexStr = _szToDecode.substr(i + , );
hex = strtol(hexStr.c_str(), , );
//字母和数字[0-9a-zA-Z]、一些特殊符号[$-_.+!*'(),] 、以及某些保留字[$&+,/:;=?@]
//可以不经过编码直接用于URL
if (!((hex >= && hex <= ) || //0-9
(hex >= && hex <= ) || //a-z
(hex >= && hex <= ) || //A-Z
hex == 0x21 || hex == 0x24 || hex == 0x26 || hex == 0x27 || hex == 0x28 || hex == 0x29
|| hex == 0x2a || hex == 0x2b || hex == 0x2c || hex == 0x2d || hex == 0x2e || hex == 0x2f
|| hex == 0x3A || hex == 0x3B || hex == 0x3D || hex == 0x3f || hex == 0x40 || hex == 0x5f
////一些特殊符号及保留字[$-_.+!*'(),] [$&+,/:;=?@]
))
{
result += char(hex);
i += ;
}else if{esult += '%';}else{result += '%';}
break;
default:
result += _szToDecode[i];
break;
}
}
return result;
}
于是很开心的完成了URL解码,开始专注 到控制台的编码问题:
从 QString UTF-8 转到 Windows cmd GBK:
string UTF8ToGBK(const char* strUTF8)
{
int len = MultiByteToWideChar(CP_UTF8, , strUTF8, -, NULL, );
wchar_t* wszGBK = new wchar_t[len + ];
memset(wszGBK, , len * + );
MultiByteToWideChar(CP_UTF8, , strUTF8, -, wszGBK, len);
len = WideCharToMultiByte(CP_ACP, , wszGBK, -, NULL, , NULL, NULL);
char* szGBK = new char[len + ];
memset(szGBK, , len + );
WideCharToMultiByte(CP_ACP, , wszGBK, -, szGBK, len, NULL, NULL);
string strTemp(szGBK);
if (wszGBK) delete[] wszGBK;
if (szGBK) delete[] szGBK;
return strTemp;
}
以及输出: 从 Windows CMD GBK 转回 UTF-8:
void Pipe::loop(){
char outbuff[]; //输出缓冲
DWORD byteread;
while (true)
{
memset(outbuff, '\0', );
if (ReadFile(this->hpiperead, outbuff, , &byteread, NULL) == NULL)break; QTextCodec *gbk1 = QTextCodec::codecForName("GBK"); //Windows 默认编码 GBK 转成 UTF-8 //主要是看这里
QString tmp = gbk1->toUnicode(outbuff); //主要是看这里
while(tmp.indexOf('\b') != -)tmp.replace('\b',""); //替换管道可能出现的乱码
//这样 Qstring tmp 就可以使用了。
memset(outbuff, '\0', );
}
}
然后差不多几个重点的问题解决了。于是继续愉快的code
但是好景不长,后来发现返回的数据在 HTTP 响应头里面总是 少了,也就是说 四个汉字 “啊啊啊啊” 变成了 “啊啊”;
我原先一直以为是编码问题,在TCP层我多层换编码输出,用UTF-8,GBK gb等等一些编码。都无果。
后来发现 是一句代码坑了这里:
//------处理数据长度--------
QString tmp_read_len;
//int i; 用前面用过的i,无需要重新那个
//Body 是QList类型
i = ;
for (line = ; line < body.size(); line++) //这个循环是 循环加入 从上层返回的数据
{
i = i + body[line].toLocal8Bit().length();
//字节数,判断中文/英文 就是因为少了toLocal8Bit,所以导致中文判断也以为是一个,实际上可能是 2 个或 4个 (UTF-8)
}
QString tmp_int_len = QString::number(i);
QString ContentLen = "Content-Length: " + tmp_int_len; //加入 Content-Length
(*list) << ContentLen;
于是解决之后,网页终于能显示“啊啊啊啊”了,于是又开始愉快的code。
可惜好景不长,在一个及其简单的地方,出现了差错。我需要实现一个 在控制台也可以操作的需求,这个很简单,用一个线程专门读取用户输入就好了
对...是很简单
//循环等待输入
void LoopCin(){
std::string com;
while (true){
char ch = '\0';
ch = getchar();
if (ch == '\n'){
//考虑多一点
if (PIPE != NULL){ //PIPE 是管道
std::cout << ">>" << com.c_str()<< std::endl;
PIPE->sendCommand(com.c_str()); //向管道发送命令 管道已经是封装好了的
com = "";
}else{
std::cout << "[程序]" << "服务器未开启,无法执行命令.请去网页后台开启您的服务器."<< std::endl;
}
}
else
{
com = com + ch; //如果不是回车 就加入char
}
}
}
于是写完这些代码之后,用C++11 的Thread 类创建线程(别问我为什么不用QThread 类,为了实现一个这个还用着那个,而且据说这个使用起来需要谨慎)
可是 用Thread创建的线程却 毫无效果,明明可以等待用户输入了,却将主线程个阻塞了,很是诧异。。
我也不是专门走C++这条路的,所以没有详细的去调查为什么,于是我用代替方法,直接使用了 WIndows API 创建线程。
PIPE_cin_hThread = CreateThread(NULL, , (LPTHREAD_START_ROUTINE)LoopCin, NULL, , &PIPE_cin_ThreadID);//创建输入循环线程
奇迹般的不知道为什么的突然就可以了。莫非 Thread 创建的线程不可靠?不是没有启动,确实启动了,但是却阻塞了主线程,整个进程在等待我输入,网页也加载不出来了。
如果你知道这个原理,还烦请告诉我一下,谢谢。
于是又开始愉快的进行code。
虽然后面还有点小插曲,但是都一一解决,完成了这个项目。关于Javascript 编写那里遇到的坑其实也没多少,就不写了。
不论是否对你有帮助,谢谢你的耐心阅读。
Qt 框架 开发HTTP 服务器 开发记录的更多相关文章
- 【Andorid开发框架学习】之Mina开发之服务器开发
下午那篇博客我们讲到了Mina的客户端的开发,如果还有没看过的同学可以看一下,我是传送门.现在,我们来学习一下,Mina的服务器的开发. 一.首先看一下,我的服务器的代码图片: 服务器代码我是在My ...
- 使用Qt框架开发http服务器问题的记录
最近需求需要开发一款 HTTP ,然后由于先前接触过Qt,就直接用Qt写HTTP服务器了,也是为了当作练手,要不然是直接上HTTP框架的. 后端用C++ Qt框架 前端为了练手 当然是纯生的 js h ...
- Qt 框架的图形性能高(OpenGL上的系统效率高),网络性能低,开发效率高,Quick是可以走硬件加速——Qt中分为好几套图形系统,差不多代表了2D描画的发展史。最经典的软描画系统
-----图形性能部分-----Qt的widgets部分,运行时的图像渲染性能是一般的,因为大部分的界面内容都是Qt自绘,没有走硬件加速,也就是说很多图形内容都是CPU算出来的.但是widgets底层 ...
- 深入浅出node.js游戏服务器开发1——基础架构与框架介绍
2013年04月19日 14:09:37 MJiao 阅读数:4614 深入浅出node.js游戏服务器开发1——基础架构与框架介绍 游戏服务器概述 没开发过游戏的人会觉得游戏服务器是很神秘的 ...
- 基于C/S架构的3D对战网络游戏C++框架_05搭建系统开发环境与Boost智能指针、内存池初步了解
本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...
- 从零开始编写自己的C#框架(2)——开发前准备工作
没想到写了个前言就受到很多朋友的支持,大家的推荐就是我最大的动力(推荐得我热血沸腾,大家就用推荐来猛砸我吧O^-^O),谢谢大家支持. 其实框架开发大家都知道,不过要想写得通俗点,我个人觉得还是挺吃力 ...
- 基于QT的webkit与ExtJs开发CB/S结构的企业应用管理系统
一:源起 1.何为CB/S的应用程序 C/S结构的应用程序,是客户端/服务端形式的应用程序,这种应用程序要在客户电脑上安装一个程序,客户使用这个程序与服务端通信,完成一定的 ...
- 笨重的mfc还在基于系统控件,熟练的mfc工程师还比不过学习Qt一个月的学生开发效率高(比较精彩,韦易笑)
作者:韦易笑链接:https://www.zhihu.com/question/29636221/answer/45102191来源:知乎著作权归作者所有,转载请联系作者获得授权. 更新:擦,本来只有 ...
- 一步一步开发Game服务器(四)地图线程
时隔这么久 才再一次的回归正题继续讲解游戏服务器开发. 开始讲解前有一个问题需要修正.之前讲的线程和定时器线程的时候是分开的. 但是真正地图线程与之前的线程模型是有区别的. 为什么会有区别呢?一个地图 ...
随机推荐
- 删除.gitignore中的在version control中的文件
如果有一个文件例如xyz在版本控制系统中,然后你发现这个文件不应该提交到git上,所以加了.gitignore文件并将其加入其中,但是git不会自动讲其从版本库中移除它.如果你只有一个文件,你可以使用 ...
- [原创]android使用代码生成LayerDrawable的方法和注意事项
为了有更好的UI体验,一般我们会把button.textview等控件的背景设置上阴影.传统的做法是美工提供一张具有阴影效果的nine patch图,然后将其在xml文件中添加到background属 ...
- Rxlifecycle(二):源码解析
1.结构 Rxlifecycle代码很少,也很好理解,来看核心类. 接口ActivityLifecycleProvider RxFragmentActivity.RxAppCompatActivity ...
- 查看iOS模拟器应用的沙箱文件
iOS 升级到8.3 以后就不能用iFunBox 这样的工具看沙箱里的文件了(非共享的), 而开发时我们的数据库文件又不在共享目录里.关于这个问题,我们可以看模拟器里的沙箱文件, iOS8.0 以后, ...
- iOS CoreData 增删改查详解
最近在学习CoreData, 因为项目开发中需要,特意学习和整理了一下,整理出来方便以后使用和同行借鉴.目前开发使用的Swift语言开发的项目.所以整理出来的是Swift版本,OC我就放弃了. 虽然S ...
- SQL 存储过程入门(事务)(四)
SQL 存储过程入门(事务)(四) 本篇我们来讲一下事务处理技术. 为什么要使用事务呢,事务有什么用呢,举个例子. 假设我们现在有个业务,当做成功某件事情的时候要向2张表中插入数据,A表,B表,我 ...
- Jquery实现ready()的源码
function bindReady(){ if ( readyBound ) return; readyBound = true; // Mozilla, Opera and webkit nigh ...
- CocoStudio教程三:认识并利用CocoStudio的果实 运行2.2.1版本
原文:CocoStudio教程三:认识并利用CocoStudio的果实 原文用的老版,用2.21搞起来好像有些问题,然后自己摸索了下,有的都是乱找的方法,只求能运行... 1,原文的CCJsonRea ...
- Android SDK开发包国内下载地址
不知道是因为最近kaihui还是怎么的,打开android sdk官方网站特别的慢,想下载最新版本的platform几乎变成不可能完成的任务,不知道为什么Google不像Apache那样在各国设立镜像 ...
- fdisk命令使用说明
CentOS我新建了几个分区,比如/dev/sda4,sda5我想挂在一个目录下,用mount /dev/sda5 /disk ,总提示mount:you must specify the fil ...