【发布时间】:2013-09-12 08:18:01
【问题描述】:
我正在用 C 语言编写一个使用套接字的 HTTP 服务器。它可以侦听多个端口并在每个端口 1 个线程的基础上运行侦听循环,每个循环都会产生另一个线程来传递响应
该代码在交付标准 HTTP 响应时运行良好。我将其设置为响应带有 JavaScript 代码的 HTML 页面,该代码只是反复刷新浏览器以对服务器进行压力测试。我已经在我的计算机作为服务器运行时对此进行了测试,另外 4 台设备同时向它发送请求。
没有崩溃,没有断开连接,也没有内存泄漏。在 HTTP 模式下运行 2.0 GHz Intel Core 2 Duo 和 4 台设备发送垃圾邮件请求时,CPU 使用率永远不会超过 5%。
我昨天刚刚添加了 OpenSSL,以便它可以通过 HTTPS 提供安全响应。进展相当顺利,因为我似乎只需要用它们的 OSSL 对应项替换一些标准套接字调用以实现安全模式(基于此问题的解决方案:Turn a simple socket into an SSL socket)。
每个连接有一个 SSL 上下文和 SSL 结构。它确实有效,但不是很可靠。同样,每个响应都发生在自己的线程上,但安全模式下的多个/快速/并发请求似乎是随机丢弃的,尽管我的代码中仍然没有崩溃或内存泄漏。
当连接断开时,浏览器要么说它正在等待一个永远不会发生的响应(Chrome),要么只是说连接已重置(Firefox)。
作为参考,这里是更新的连接创建和关闭代码。
连接创建代码(监听循环的主要部分):
// Note: sslCtx and sslConnection exist
// elsewhere in memory allocated specifically
// for each connection.
struct sockaddr_in clientAddr; // memset-ed to 0 before accept
int clientAddrLength = sizeof(clientAddr);
...
int clientSocketHandle = accept(serverSocketHandle, (struct sockaddr *)&clientAddr, &clientAddrLength);
...
if (useSSL)
{
int use_cert, use_privateKey, accept_result;
sslCtx = SSL_CTX_new(SSLv23_server_method());
SSL_CTX_set_options(sslCtx, SSL_OP_SINGLE_DH_USE);
use_cert = SSL_CTX_use_certificate_file(sslCtx, sslCertificatePath , SSL_FILETYPE_PEM);
use_privateKey = SSL_CTX_use_PrivateKey_file(sslCtx, sslCertificatePath , SSL_FILETYPE_PEM);
sslConnection = SSL_new(sslCtx);
SSL_set_fd(sslConnection, clientSocketHandle);
accept_result = SSL_accept(sslConnection);
}
... // Do other things and spawn request handling thread
连接关闭代码:
int recvResult = 0;
if (!useSSL)
{
shutdown(clientSocketHandle, SHUT_WR);
while (TRUE)
{
recvResult = recv(clientSocketHandle, NULL, 0, 0);
if (recvResult <= 0) break;
}
}
else
{
SSL_shutdown(sslConnection);
while (TRUE)
{
recvResult = SSL_read(sslConnection, NULL, 0);
if (recvResult <= 0) break;
}
SSL_free(sslConnection);
SSL_CTX_free(sslCtx);
}
closesocket(clientSocketHandle);
同样,这对于 HTTP 响应来说是 100% 完美的。 HTTPS 响应可能出了什么问题?
更新
我已经使用多线程环境的 OpenSSL 回调更新了代码,并且使用来自以下问题的答案的代码:OpenSSL and multi-threads,服务器稍微更可靠。
我编写了一个小型命令行程序,通过 HTTPS 请求向服务器发送垃圾邮件,并且它不会在同时运行 5 个多个实例时断开任何连接。多个 Firefox 实例似乎也没有断开任何连接。
然而,有趣的是,现代基于 WebKit 的浏览器仍在断开连接。 Chrome 在垃圾邮件发送不到 30 秒时开始断开连接,iPhone 4 (iOS 5.1) 上的 Safari 在说连接丢失之前很少刷新 3 次,但 iPad 2 (iOS 5.0) 上的 Safari 似乎应对时间最长但最终也会断开连接。
【问题讨论】:
-
您使用的是阻塞 BIO 还是非阻塞 BIO?
-
我没有专门设置任何 BIO,但我会假设默认设置是阻塞的,因为我可以看到侦听器线程必须等待通过
accept的连接才能执行其他任何操作。附言我在底部更新了问题,提供了更多信息。 -
虽然您已经声明每个侦听端口都使用一个线程,但不清楚您是否每个连接都使用一个线程,这就是我问的原因。如果每个连接都使用一个线程,那么您将需要锁。 SSL 握手是计算密集型的。我猜你的垃圾邮件发送者可能没有使用 SSL 会话缓存,所以这会导致你的服务器使用最大数量的 CPU。这将导致它在服务其他连接或新的传入连接方面 CPU 不足。
-
感谢您提供此信息,我不知道基于每个上下文运行的会话缓存。我更改了程序的结构以保留和重用 SSL 上下文,而不是为每个新连接重新创建它们。没有安全连接被丢弃,一切都比以前更快。如果您以答案的形式发表评论,我可以将其标记为已解决,感谢您:)
标签: c connection openssl httpserver