我正在使用套接字在C中编写HTTP服务器。它可以侦听多个端口,并以每个端口1个线程为基础运行侦听循环,每个循环生成另一个线程以提供响应
代码在提供标准HTTP响应时非常有效。我已将其设置为使用包含JavaScript代码的HTML页面进行响应,该代码仅重复刷新浏览器以便对服务器进行压力测试。我用我的计算机作为服务器进行了测试,其他4个设备同时向请求发送垃圾邮件。
没有崩溃,连接断开,没有内存泄漏。在HTTP模式下,2.0 GHz Intel Core 2 Duo上的CPU使用率从未超过5%,其中有4个设备发出垃圾邮件请求。
我昨天刚刚添加了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响应非常有效。 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似乎应对时间最长但是最终也会失去联系。
答案 0 :(得分:1)
您应该在请求处理线程中调用SSL_accept()
。这将允许您的侦听线程更快地处理TCP接受/侦听队列,并减少由于完整的接受/侦听队列而新连接从TCP堆栈获得RESET的机会。
SSL握手是计算密集型的。我猜你的垃圾邮件发送者可能没有使用SSL会话缓存,因此这会导致您的服务器使用最大量的CPU。这将导致它在维修其他连接或新的传入连接时缺乏CPU。