带 HTTP 服务器示例的多线程服务器类






4.79/5 (25投票s)
2001 年 8 月 13 日
4分钟阅读

190590

10383
带 HTTP 服务器示例的多线程服务器类
引言
本文涵盖以下主题
- Win32 线程
- 同步对象
- 异步套接字
- 基本 HTTP 协议
- 使用抽象类
- STL
详细说明
在编写套接字服务器时,我必须在单线程技术和多线程技术之间进行选择。我发现多线程技术有几个优点,包括以下几点:- 使用
WSAWaitForMultipleObjects(...)
时,无需处理 64 个句柄的限制。 - 无需费力地将大文件传递给客户端而不延迟其他请求。
实现
有一定数量的线程始终处于活动状态,等待或处理某些事务。第一个重要的线程是AcceptThread
。它的职责是初始化套接字库并监听所有传入的连接。每次有新连接到达时,都会创建一个新线程。这个新线程称为 ClientThread
,它的职责是处理新连接客户端的需求。当客户端断开连接时,关闭线程句柄的请求将通过 HandleList 对象传递给 HelperThread。接下来我将描述 CGenericServer
类中的一些重要函数的设计目的。这些函数按照它们在源代码中的实现顺序出现。
GetStats(...) -
返回有关流量、连接的唯一 IP 地址的访问者数量、服务器的总请求数以及当前连接到服务器的客户端数量等有用统计信息。
AddClient(...)
- 通知派生类新的连接到达,并创建一个工作线程来处理子套接字的需求。
Run(...) -
启动 Accept 和 Helper 线程。
Shutdown(...) -
终止 Accept 和 Helper 线程,并等待 ClientThreads 也被终止。
Reset(...) -
将统计值重置为 0。
CleanupThread(...) -
清理ClientThread
使用的所有已打开句柄和已分配内存。线程 ID 将传递给HelperThread
以便通过此函数进一步终止。仅由ClientThread
使用。
CleanupThread(...) -
关闭主监听套接字和一些句柄。仅由AcceptThread
使用。
AcceptThread(...)
- 处理所有传入连接的主工作线程。
ClientThread(...)
- 处理所有已建立的客户端连接。
HelperThread(...) -
如果我没有使用_endthread(...)
,则需要有人在线程不再活动后关闭其句柄。该函数被分配给HelperThread
。
如果你的类派生自 CGenericServer
,则应实现以下抽象函数:
IsComplete(...) -
这是一个非常重要的函数,需要仔细实现。你必须根据调用此函数时收到的数据来确定整个消息是否已在缓冲区中。在我提供的 HTTP 服务器示例中,条件是双 \r\n。
ParseRequest() -
这也是一个非常重要的函数。它将向你提供从客户端接收到的数据,并需要你返回响应数据。如果你返回 false,连接将立即关闭,线程也将被终止。否则,连接将保持打开状态,除非你将来将KeepAlive
变量设置为 1。你将在我的示例中找到基本的 HTTP 实现。
GotConnection(...) -
这将向你提供连接客户端的 IP 地址和端口号。
DataSent(...) -
每次将数据发送到客户端时,它都会通知你,并给出发送的字节数。
补充说明
我的 HTTP 服务器支持两个最常见的 HTTP 错误:
- 404 资源未找到
- 504 方法未实现
这些错误都有相应的 HTML 文件。这些文件的名称目前硬编码在 HTTPServer.h 中。这些文件必须位于服务器的根目录下。
我还向CHTTPServer
类添加了几个 MIME 类型,以便浏览器可以识别这些类型。当然,还有更多可用的类型。一个简单的 CLog
类被用来记录可能发生的任何错误。日志文件将放置在 Windows 目录下,默认文件名是 UMServer.log。这不是一个功能齐全的 HTTP 服务器,也没有实现 HTTP 协议的所有功能,因为这不是本项目的目标。