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() * 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]、一些特殊符号[$-_.+!*‘(),] 、以及某些保留字[$&+,/:;[email protected]]
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                     ////一些特殊符号及保留字[$-_.+!*‘(),]  [$&+,/:;[email protected]]
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 编写那里遇到的坑其实也没多少,就不写了。

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

时间: 2025-01-02 06:51:37

Qt 框架 开发HTTP 服务器 开发记录的相关文章

Qt 框架的图形性能高,网络性能低,开发效率高——Qt中分为好几套图形系统,差不多代表了2D描画的发展史。最经典的软描画系统

-----图形性能部分-----Qt的widgets部分,运行时的图像渲染性能是一般的,因为大部分的界面内容都是Qt自绘,没有走硬件加速,也就是说很多图形内容都是CPU算出来的.但是widgets底层毕竟是C++,而且Qt的模块写的也不错,做过很多优化,这个渲染的性能在桌面上与有硬件加速的框架比差别不大,除非是有很多动画的复杂场景才能看出区别.不过在手机上或者嵌入式上,就会明显觉得widgets的渲染性能低了. 那么怎么办呢,Qt是不会抱死在widgets一个框架上的.所以Qt推出了Quick和

基于C/S架构的3D对战网络游戏C++框架 _05搭建系统开发环境与Boost智能指针、内存池初步了解

本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): 1.实现基本通信框架,包括对游戏的需求分析.设计及开发环境和通信框架的搭建: 2.实现网络底层操作,包括创建线程池.序列化网络包等: 3.实战演练,实现类似于CS反恐精英的3D对战网络游戏: 技术要点:C++面向对象思想.网络编程.Qt界面开发.Qt控件知识.Boost智能指针.STL算法.STL.

从零开始编写自己的C#框架(2)——开发前准备工作

原文:从零开始编写自己的C#框架(2)--开发前准备工作 没想到写了个前言就受到很多朋友的支持,大家的推荐就是我最大的动力(推荐得我热血沸腾,大家就用推荐来猛砸我吧O^-^O),谢谢大家支持. 其实框架开发大家都知道,不过要想写得通俗点,我个人觉得还是挺吃力的.在本系列中,我不会很深入的去讲述那些映射啊关系啊(绕得我头都晕了,这些请大家去查看相关的文档学习吧)......概念上的东西会少讲,直接上实例,讲实际应用.当然如果您觉得这样很不尊重面向对象什么的,那也没办法,只能说我们不在一个频道上.

游戏服务器开发需要学习的技术

一,游戏服务器编程语言的选择 所谓的游戏服务器编程语言其实有很多,基本上任何一种语言都可以作为游戏服务器的编程语言.这需要根据自己游戏的类型和要求加以选择.比如C++,Java ,Erlang,go等等.目前我用过的只有C++和Java.但是以Java为主.所以接下来就以自己的经验,谈谈以Java为核心的游戏服务器开发技术体系. Java目前作为游戏服务器开发语言已经很是普遍.但是大多数是作为页游或手游的服务器,而端游戏一般选择C++,因为端游对服务器的性能要求相对比较高一些.两种语言各有利弊.

NIO原理剖析与Netty初步----浅谈高性能服务器开发(一)

除特别注明外,本站所有文章均为原创,转载请注明地址 在博主不长的工作经历中,NIO用的并不多,由于使用原生的Java NIO编程的复杂性,大多数时候我们会选择Netty,mina等开源框架,但理解NIO的原理就不重要了吗?恰恰相反,理解NIO底层机制是理解这一切的基础,由此我总结一下当初学习NIO时的笔记,以便后续复习. 以下是我理解的Java原生NIO开发大致流程: 上图大致描述的是服务端的NIO操作. 第一步,绑定一个服务的端口 这与传统阻塞IO中的ServerSocket类似,没什么好说的

手游服务器开发技术详解

从事游戏服务器开发差不多两年时间,两年间参与了不少项目,学到了很多游戏服务器开发技术,参与过几个不同架构的服务器开发,就随便聊聊游戏服务器开发需要的技术.(以下所指游戏服务器更偏向于手游,因为我对端游和页游开发接触并不多) 一.聊聊服务器开发有哪些东西要考虑. 1.开发语言的选择: 工欲善其事,必先利其器,选择一门适合的开发语法对后期开发有着事半功倍的作用. 业界主要的是c/c++ + Python/lua模式做游戏服务器.c/c++做网络通讯数据传输,python/lua做业务逻辑.这样既保持

JAVA+PHP+阿里云组件纯手工实现POP、SMTP、IMAP开发邮件服务器(二)

java开发邮件服务器的接收模块 用java建立socket服务端,监听端口25,实现SMTP协议.即可完成邮件服务器的接收模块. 这里要注意的是,SMTP协议其实可以分为两种.一种是你用手机.PC等客户端发邮件到邮件服务商的服务器的时候用的SMTP协议,这一类是需要登录验证的.一种是邮件服务商之间传递邮件的SMTP协议,此类协议是不需要登录的.比如你用Foxmail上你的QQ邮箱发送了一封邮件到163的邮箱.过程是这样的: 邮件从Foxmail通过SMTP协议发送到QQ邮箱的服务器. QQ邮箱

?Jersey 开发RESTFUL服务器

?Jersey 开发RESTFUL服务器 RESTful 一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制. REST(英文:Representational State Transfer,简称REST)描述了一个架构样式的网络系统,比如 web 应用程序.REST相比于SOAP(Simple Object Access protocol,简单对象访问协议)以及XML-R

MAPR 开发环境搭建过程记录

我下载了MAPR 官方提供的virtualbox 和 vmware版本的sandbox进行试用. 开始试用了一会vmware版的,因为不太熟悉vmware的操作,而且vmplayer经常没有反应,后来改用了virtualbox版. 因为sandbox是单机版的,所以必须把网络设置设为host only,否则服务是无法正常启动的. 即使是这样,服务时常因为超时无法正常启动,这时我们可以在按alt+F2进入系统后重启服务即可.可能需要多尝试几次. 我想在sandbox中使用eclipse开发mapr