最近需求需要开发一款 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() * 65535 / 2);int n = qrand();QString tmp = QString::number(n, 1000);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(6, 28);        //取用户名前面这一段   不完整QString rand = randcookie.mid(33,97);   //随机数QString passhead = randcookie.mid(72, 20);    //这里去掉前8位    //取中间这一段   不给完整的 MD5码randcookie = rand+ userhead + passhead;//随机数 账号 密码randcookie += "_Hi_hacker";       //向大牛打声招呼return randcookie;
}

如果要是再看的你发现了漏洞,一定要留言告诉我,我将立即改进。

随后我们遇到了编码问题。

一个问题就是,因为我们的需求包括了 网页操作控制台(用匿名管道实现,详情可以看我的这篇文章:http://www.cnblogs.com/suwings/p/5754943.html,顺带一提,Qt框架也可以实现,只是由于时间问题,没法再做描述)

但是Windows 控制台默认是 GBK 编码,这将导致一个问题的出现,中文输入的数据可能将乱码。输出的数据也可能将乱码。

不过在我们测试的发现居然忘记URL中文解码了,但是Qt自带的解码有个问题就是 英文有时候也会一起解码。

于是在网上找到了如下实现方法:

 1 /************************************************************************/2 /* URL解码                          英文可不解                                             */3 /************************************************************************/4 std::string urlDecode(const std::string & _szToDecode)5 {6     std::string result;7     int hex = 0;8     for (size_t i = 0; i < _szToDecode.length(); ++i)9     {
10         switch (_szToDecode[i])
11         {
12         case '+':
13             result += ' ';
14             break;
15         case '%':
16             if (isxdigit(_szToDecode[i + 1]) && isxdigit(_szToDecode[i + 2]))
17             {
18                 std::string hexStr = _szToDecode.substr(i + 1, 2);
19                 hex = strtol(hexStr.c_str(), 0, 16);
20                 //字母和数字[0-9a-zA-Z]、一些特殊符号[$-_.+!*'(),] 、以及某些保留字[$&+,/:;=?@]
21                 //可以不经过编码直接用于URL
22                 if (!((hex >= 48 && hex <= 57) || //0-9
23                     (hex >= 97 && hex <= 122) ||   //a-z
24                     (hex >= 65 && hex <= 90) ||    //A-Z
25                     hex == 0x21 || hex == 0x24 || hex == 0x26 || hex == 0x27 || hex == 0x28 || hex == 0x29
26                     || hex == 0x2a || hex == 0x2b || hex == 0x2c || hex == 0x2d || hex == 0x2e || hex == 0x2f
27                     || hex == 0x3A || hex == 0x3B || hex == 0x3D || hex == 0x3f || hex == 0x40 || hex == 0x5f
28                     一些特殊符号及保留字[$-_.+!*'(),]  [$&+,/:;=?@]
29                     ))
30                 {
31                     result += char(hex);
32                     i += 2;
33                 }else if{esult += '%';}else{result += '%';}
34             break;
35         default:
36             result += _szToDecode[i];
37             break;
38         }
39     }
40     return result;
41 }

于是很开心的完成了URL解码,开始专注 到控制台的编码问题:

从 QString UTF-8 转到 Windows cmd GBK:

 1 string UTF8ToGBK(const char* strUTF8)2 {3     int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, NULL, 0);4     wchar_t* wszGBK = new wchar_t[len + 1];5     memset(wszGBK, 0, len * 2 + 2);6     MultiByteToWideChar(CP_UTF8, 0, strUTF8, -1, wszGBK, len);7     len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);8     char* szGBK = new char[len + 1];9     memset(szGBK, 0, len + 1);
10     WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
11     string strTemp(szGBK);
12     if (wszGBK) delete[] wszGBK;
13     if (szGBK) delete[] szGBK;
14     return strTemp;
15 }

以及输出: 从 Windows CMD GBK 转回 UTF-8:

 1 void Pipe::loop(){2     char outbuff[4096];        //输出缓冲3     DWORD byteread;4     while (true)5     {6         memset(outbuff, '\0', 4096);7         if (ReadFile(this->hpiperead, outbuff, 4095, &byteread, NULL) == NULL)break;8 9         QTextCodec *gbk1 = QTextCodec::codecForName("GBK");        //Windows 默认编码 GBK 转成 UTF-8 //主要是看这里
10         QString tmp = gbk1->toUnicode(outbuff);                    //主要是看这里
11         while(tmp.indexOf('\b') != -1)tmp.replace('\b',"");        //替换管道可能出现的乱码
12      //这样 Qstring tmp 就可以使用了。
13         memset(outbuff, '\0', 4096);
14     }
15 }

然后差不多几个重点的问题解决了。于是继续愉快的code

但是好景不长,后来发现返回的数据在 HTTP 响应头里面总是 少了,也就是说 四个汉字 “啊啊啊啊” 变成了 “啊啊”;

我原先一直以为是编码问题,在TCP层我多层换编码输出,用UTF-8,GBK gb等等一些编码。都无果。

后来发现 是一句代码坑了这里:

    //------处理数据长度--------QString tmp_read_len;//int i;  用前面用过的i,无需要重新那个//Body 是QList类型i = 0;for (line = 0; 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。

可惜好景不长,在一个及其简单的地方,出现了差错。我需要实现一个 在控制台也可以操作的需求,这个很简单,用一个线程专门读取用户输入就好了

对...是很简单

 1 //循环等待输入2 void LoopCin(){3     std::string com;4     while (true){5         char ch = '\0';6         ch = getchar();7         if (ch == '\n'){8                 //考虑多一点9             if (PIPE != NULL){    //PIPE 是管道
10                 std::cout << ">>" << com.c_str()<< std::endl;
11                 PIPE->sendCommand(com.c_str());        //向管道发送命令 管道已经是封装好了的
12                 com = "";
13             }else{
14                 std::cout << "[程序]" << "服务器未开启,无法执行命令.请去网页后台开启您的服务器."<< std::endl;
15             }
16         }
17         else
18         {
19             com = com + ch;        //如果不是回车 就加入char
20         }
21     }
22 }

于是写完这些代码之后,用C++11 的Thread 类创建线程(别问我为什么不用QThread 类,为了实现一个这个还用着那个,而且据说这个使用起来需要谨慎)

可是 用Thread创建的线程却 毫无效果,明明可以等待用户输入了,却将主线程个阻塞了,很是诧异。。

我也不是专门走C++这条路的,所以没有详细的去调查为什么,于是我用代替方法,直接使用了 WIndows API 创建线程。

1  PIPE_cin_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)LoopCin, NULL, 0, &PIPE_cin_ThreadID);//创建输入循环线程

奇迹般的不知道为什么的突然就可以了。莫非 Thread 创建的线程不可靠?不是没有启动,确实启动了,但是却阻塞了主线程,整个进程在等待我输入,网页也加载不出来了。

如果你知道这个原理,还烦请告诉我一下,谢谢。

于是又开始愉快的进行code。

虽然后面还有点小插曲,但是都一一解决,完成了这个项目。关于Javascript 编写那里遇到的坑其实也没多少,就不写了。

不论是否对你有帮助,谢谢你的耐心阅读

使用Qt框架开发http服务器问题的记录相关推荐

  1. NASA 选择 Qt 框架开发国际空间站应用

    Qt 跨平台应用和 UI 开发框架目前被 NASA International Space Apps Challenge 标识为推荐的应用框架. 在商业公司 Digia 的推动下,目前已经超过 100 ...

  2. Day644.Spring框架开发双刃剑 -Java业务开发常见错误

    Spring框架开发双刃剑 Hi,阿昌来也,今天学习记录的是针对Spring框架开发双刃剑的学习文章记录. Spring 框架内部的复杂度主要表现为三点: 第一,Spring 框架借助 IoC 和 A ...

  3. C++/Qt框架下的简易计算器

    C++/Qt框架下的简易计算器 1.简易计算器-介绍 该项目目标是设计开发一个支持连续计算的简易计算器,通过单击按钮,输入并完成如4+5+6或5×8+16类似的各种连续计算,并将运算结果显示在输出文本 ...

  4. Qt跨平台开发环境搭建

    Qt跨平台开发环境搭建 1.     背景介绍 Qt是一个跨平台的C++图形用户界面应用程序框架.它提供给应用程序开发者丰富的图形用户界面所需的所有功能.而且,Qt很容易扩展,并且允许真正地组件编程. ...

  5. Ubuntu下基于Qt框架,使用WebRtc开发总结(二):Qt工程配置总结

    .Pro文件的配置 那么,接上一部分,根据自己是否需要显示视频,新建一个Qt的控制台/窗口程序,并根据自己的开发需要添加Qt的组件,我的工程添加了如下的组件: QT += core websocket ...

  6. 基于QT框架的软件开发

    QT入门科普 初识QT 更新到QT6: QT的许可类型 QT与C++ QT美与丑 技术选型偏重 未来发展趋势 个人发展路线 初识QT Qt 是一个1991年由Qt Company开发的跨平台C++图形 ...

  7. Qt+QtWebApp开发笔记(一):QtWebApp介绍、下载和搭建基础封装http轻量级服务器Demo

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/130631547 红胖子网络科技博文大全:开发技术集合( ...

  8. 游戏服务器Mina框架开发

    游戏服务器Mina框架开发 作者:老九-技术大黍 社交:知乎 公众号:老九学堂(新人有惊喜) 特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权 前言 如果要使用Java语言来开发游戏 ...

  9. PySide是Python语言的Qt框架的一个绑定。PySide支持跨平台和本地GUI应用程序开发,是在Python 2.6、2.7和3.x版本下可用。

    PySide是Python语言的Qt框架的一个绑定.PySide支持跨平台和本地GUI应用程序开发,是在Python 2.6.2.7和3.x版本下可用. 在开始使用PySide之前,需要在你的机器上安 ...

  10. Qt嵌入式开发的基本认识

    目录 目录 1.Qt简介 2.Qt开发计算器的基本方法 2.1Qt常用快捷键 2.2QPushbutton的创建 2.3.QPushButton按钮的显示 2.4.使用栈和队列将中缀表达式转化为后缀表 ...

最新文章

  1. AbstractFactory抽象工厂模式
  2. 生产事故 java_记一次生产事故:30万单就这样没了!
  3. 科大星云诗社动态20201225
  4. java media_unmount file_(20120801)android文件的读写SD卡总结
  5. Storm配置文件中主要参数配置说明
  6. Spring Reactive已经过时了吗? 螺纹连接反转
  7. 如何添加自动更新Play Framework 2.X项目的版本号
  8. 【C++模板】特化与偏特化 template [partial] specialization
  9. @staticmethod和@classmethod的作用与区别
  10. 【ES】ES 如何在一个机器上同时模拟多个node
  11. Kafka : 查看kafka topic的消息offset范围
  12. 二维码相关---java生成二维码名片,并且自动保存到手机通讯录中...
  13. globalmapper如何选取图像上的点_图像配准算法
  14. 7-19 求链式线性表的倒数第K项
  15. Elasticsearch的索引模块(正排索引、倒排索引、索引分析模块Analyzer、索引和搜索、停用词、中文分词器)...
  16. FCM聚类算法(模糊C均值算法)
  17. 在三角形中rt是什么意思_什么叫Rt三角形
  18. 长春市职称计算机考试成绩查询,长春市助理工程师查询网站
  19. 使用Route报错:A <Route> is only ever to be used as the child of <Routes> element, never rendered directl
  20. [经验教程]2022京东618红包活动时间是什么时候开始什么时候结束及怎么领取京东618红包?

热门文章

  1. JDBCUtils——DBCP
  2. noip模拟赛 蒜头君打地鼠
  3. SQLite.dll混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集。...
  4. Android so文件进阶 一
  5. 英特尔发布全新英特尔® INDE 2015工具套件
  6. 常用数据类型使用转换详解
  7. vbs 读unicode 编码格式的文件
  8. ubantu中让g++支持c++11的办法
  9. Django-admin源码流程
  10. php工程师各大公司要求