HTTP服务器实现

基于QT的QTcpServer类实现简单的HTTP服务器Tinyhttpd。代码存放在Github

QThread和QTimer设置

当子类化QThread时,构造函数在旧线程中执行,然而run()在新线程中执行。如果一个成员变量的访问来自两个函数,然后从两个不同的线程访问变量,需要检查这样做是否安全。

QTimer不能在一个线程里实例化,而在另一个线程start或者stop。采取的方法是:

QTimer* keep_alive_timer;

void Request::run()
{
    keep_alive_timer = new QTimer();
    if (s_keep_alive_enable)
    {
        keep_alive = s_keep_alive_default;
        keep_alive_timeout = s_keep_alive_timeout * 1000;// the wait time
        connect(keep_alive_timer,SIGNAL(timeout()),this,SLOT(onTimeout()));
        keep_alive_timer->setSingleShot(true);
        keep_alive_timer->setInterval(keep_alive_timeout);
        keep_alive_timer->start();
    }
    socket = new QTcpSocket();
    if (!socket->setSocketDescriptor(socketDescriptor))
        return;
    connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()),
    Qt::DirectConnection);
    connect(socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()),
    Qt::DirectConnection);
    exec();
}

QT事件循环机制

在上面的run()之后有一个exec()函数,它会进入事件循环。

Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环。该循环可以简化的描述为如下的代码:

while ( !app_exit_loop )
{
   while( !postedEvents ) { processPostedEvents() }
   while( !qwsEvnts ){ qwsProcessEvents();   }
   while( !postedEvents ) { processPostedEvents() }

}

先处理Qt事件队列中的事件,直至为空。再处理系统消息队列中的消息,直至为空。在处理系统消息的时候会产生新的Qt事件,需要对其再次进行处理。

QT垃圾回收机制

在程序中有很多地方new了一个对象并没有delete,这样会不会造成内存泄漏呢。

在QT中不会,它实现了自己的GC机制。

所有继承自QObject类的类,如果在new的时候指定了父亲,那么它的清理是在父亲被delete的时候。如果一个程序中,所有的QObject类都指定了父亲,那么他们是会一级级的在最上面的父亲清理时被清理。我们需要显式释放的是那些没有父对象的孤立的指针对象。

多端口监听实现

port的配置文件这么定义

[httpd]
port=1234|1235|1236|1237

读取配置

bool ok;
QString t_port = Settings::instance().value("httpd/port").toString();
QStringList port_list = t_port.split("|");

for(int i = 0; i < port_list.size(); i++)
    {
        int port = port_list[i].toInt(&ok,10);
        startInstance(port);
    }
    return true;

注意listen(QHostAddress::Any, 1234)指监听任何连上1234端口的IP。

incomingConnection虚函数

它是一个虚函数,当服务器收到连接请求时,它被QTcpServer调用。它在底层创建一个QTcpSocket,设置socket描述符并把描述符加入内部的列表(内部可能用select进行数据异步读写)。当重载这个函数时,可以改变服务器收到连接请求时的行为,相当于实现了一个hook。这样就可以在这个函数里实现http服务器。

void Server::incomingConnection(int socketDescriptor)
{
    //有新的连接时,代替默认的功能(新建socket)
    Request * request = new Request(socketDescriptor);
    request->start();
}

可见socket描述符传到了Request实例里,这个描述符可能是实现多client并发连接的关键。

Request类

Request是QThread的子类,它对每一个描述符新建一个线程来处理它,实质就是处理每个client的请求。Request类的核心是Request::run(),它还实现了incommingConnection新建socket的base功能,通过start()来进行调用。

void Request::run()
{
    socket = new QTcpSocket();
    //用socket描述符作为新socket的描述符
    if (!socket->setSocketDescriptor(socketDescriptor))
        return;
    connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()),
    Qt::DirectConnection);
    exec();
}

这个函数里需要用socketDescriptor作为新QTcpSocket对象的描述符。

If you want to handle an incoming connection as a new QTcpSocket object in another thread you have to pass the socketDescriptor to the other thread and create the QTcpSocket object there and use its setSocketDescriptor() method.

注意这个函数的最后一行是exec(),会进入事件循环,ServerThread::run()中也有exec()函数。

当数据准备好可读的时候,内核select发出readyRead()信号触发onReadyRead()函数。这样看来,onReadyRead()是一脉相承下来最重要的函数。

onReadyRead函数

这个函数里开始解析client发过来的request数据,并构造封装response数据。

这里是用浏览器抓取的标准的Request Headers和Response Headers。

onReadyRead函数里的执行顺序如下:

  1. getRequestHeader
    负责读取request headers,确认client要求的是长\短连接。
  2. tryResponseFile
    返回请求路径的目录,有4种情况。ResponseDirectory()返回目录;直接返回页面;404;403
  3. ResonseFile
    调用response->response返回网页
  4. clearStatus
    如果是长连接,clearStatus()后继续socket监听。同时计时,如果超过timeout则删除socket连接。如果是短连接,直接删除socket连接。

总结

Singleton Pattern用在日志系统和配置系统中,这个例子中不是线程安全的。

Reference

[1].http://blog.chinaunix.net/uid-27685749-id-3847998.html

[2].http://www.linuxidc.com/Linux/2011-03/33810p2.htm

[3].http://blog.csdn.net/envenler/article/details/8020064

[4].http://blog.csdn.net/jandunlab/article/details/14108595

[5].http://blog.sina.com.cn/s/blog_a6fb6cc90101hohu.html

[6].http://qimo601.iteye.com/blog/1407911

时间: 2024-12-24 03:51:41

HTTP服务器实现的相关文章

Sqlserver通过链接服务器访问Oracle的解决办法

转自http://blog.sina.com.cn/s/blog_614b6f210100t80r.html 一.创建sqlserver链接服务(sqlserver链接oracle)  首先sqlserver 链接oracle可以通过两个访问接口: “MSDAORA” 和“OraOLEDB.Oracle” 1.“MSDAORA”访问接口是由Microsoft OLE DB Provider for Oracle提供的,这里建议不使用此接口进行链接.通过该访问接口建立的链接服务器在进行查询orac

XShell 连接虚拟机中的服务器 失败 、连接中断(Connection closed by foreign host.)

在使用XShell连接虚拟机中的服务器时,报以下错误并断开连接,之前连接还是挺稳定的,忽然就这样了 Last login: Thu Aug 10 21:28:38 2017 from 192.168.1.102 [[email protected] ~]# Socket error Event: 32 Error: 10053. Connection closing...Socket close. Connection closed by foreign host. Disconnected f

通过SoftEther VPN自建VPN服务器

SoftEther VPN是日本政府的研究和开发项目的一项工作,由日本的经济.贸易和工业部资助,由信息化推进机构管理.SoftEther VPN在日本筑波大学开发的免费软件.具有终极兼容许多设备的高性能VPN.支持Windows.Mac.智能手机.平板电脑(iPhone\iPad\Android\WindowsRT)和思科或其他VPN路由器.SoftEther VPN也接受OpenVPN和MS-SSTP VPN客户端. 可以从http://www.softether-download.com/(

微信开发之本地接口调试(非80端口,无需上传服务器服务器访问

前言: 本文是总结在开发微信接口时,为方便开发所采取的一些快捷步骤,已节省开发人员难度开发时间从而提高开发效率. 本地测试: (提醒,在本地模拟微信get或post数据时先将校验参数注释掉) 利用微信模拟器weixinPost进行模拟发送xml数据 由于这个软件是微信没公开高级接口时公布的,所以对于高级接口的调试,笔者利用的是Fiddler这个抓包工具模拟post数据 高端测试: 利用反向代理软件ngrok访问本地项目: ngrok 是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之

pfsense Web服务器负载平衡配置示例

在pfsense的网关和服务器中有两种类型的负载平衡功能.网关负载平衡可以通过多个WAN连接分发Internet绑定的流量.服务器负载平衡管理传入流量,因此它利用多个内部服务器进行负载分配和冗余,服务器负载平衡允许流量在多个内部服务器之间分配,它最常用于Web服务器和SMTP服务器.下面我们就以实例来介绍服务器负载平衡的设置. 下面介绍如何通过pfsense2.32配置Web服务器的负载平衡. 网络环境 服务器负载平衡示例网络环境 上图为示例网络环境.它由单个防火墙组成,使用其WAN IP地址池

部署AlwaysOn第一步:搭建Windows服务器故障转移集群

在Windows Server 2012 R2 DataCenter 环境中搭建集群之前,首先要对Windows服务器故障转移集群(Windows Server Failover Cluster,简称WSFC)有基本的了解.WSFC必须部署在域管理环境中,由多台服务器组成,每台服务器称作一个"结点"(Node),每个结点上都运行了Windows服务器故障转移集群服务,整个集群系统允许部分结点掉线.故障或损坏而不影响整个系统的正常运作.集群自动检测结点的健康状态,一旦活跃结点发生异常,变

如何将Win7做为NTP服务器

1. 修改注册表项 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer Enabled 设定为 1(默认0) HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config\ AnnounceFlags 设定为 5 (默认 10) Value Meaning 0 Timeserv_Announce_No, R

win7搭建局域网时间服务器

近日,本人想在局域网内通过普通的windows 7 PC搭建一台NTP服务器,可看似简单的配置却给我捣腾了了半天.初期,参考了互联网的上相关的配置文档,可网络设备就是死活不同步NTP服务器的时间.实在没办法,只有通过来抓包分析了,经过一番研究后,终于找到问题,现将这个文档与大家分享: 通过windows系统为局域网搭建NTP服务器,为局域网内网络设备提供时间服务,经过测试,使用于windows xp.windows 2003.windows 7. 1.启用 NTPServer.为此,请按照下列步

C# 远程服务器 安装、卸载 Windows 服务,读取远程注册表,关闭杀掉远程进程

这里安装windows服务我们用sc命令,这里需要远程服务器IP,服务名称.显示名称.描述以及执行文件,安装后需要验证服务是否安装成功,验证方法可以直接调用ServiceController来查询服务,也可以通过远程注册表来查找服务的执行文件:那么卸载文件我们也就用SC命令了,卸载后需要检测是否卸载成功,修改显示名称和描述也用sc命令.至于停止和启动Windows服务我们可以用sc命令也可以用ServiceController的API,当停止失败的时候我们会强制杀掉远程进程,在卸载windows

C# 远程服务器 创建、修改、删除 应用程序池 网站

首先 C# 操作 站点 需要 引用Microsoft.Web.Administration.dll 文件,创建站点我们一般需要 远程服务的IP,网站名称.端口.物理路径:这里默认网站名称和应用程序池名称一致. 应用程序池默认不启动,应为刚创建站点是没有对应真实的物理文件,修改 队列长度.启动模式.回收时间.最大工作进程, 以及日志路径.修改的时候如果修改站点物理路径的话,我们需要把文件 从旧得目录拷贝到新的目录下,删除站点就比较简单了. 但是站点应用程序池的停止 和启动就比较难搞了,不是调用st