c++ - 在多线程 HTTP 服务器中发送后如何干净地关闭套接字?

标签 c++ c multithreading sockets

我继承了一个 Windows C/C++ 代码 TCP/IP 服务器来维护,其中以前的程序员定义了一个额外的端口用于基本的 HTTP 通信。服务器使用 Windows 套接字库 2,通过生成新的发送线程来处理请求。问题似乎是,对包含多个项目的 HTML 页面的 HTTP 请求似乎无法完全加载该页面,这意味着客户端会在 Web 浏览器中看到一个无休止旋转的图标。

搞乱代码后我发现问题在于客户端(例如 Chrome v33)在同一端口上发出多个 GET 请求,这意味着服务器因此启动新线程来处理同一套接字上的这些请求。也就是说,accept() 返回的套接字(即连接套接字,而不是监听套接字)随后被传递给 CreateThread() 用户函数来处理 GET 请求。但是,第一个启动的线程在完成发送后会关闭此套接字,这意味着其他线程在轮到发送时发现套接字已关闭。我尝试不关闭套接字,这效果更好并加载了更多页面,但仍然没有完全完成页面加载,大概是因为没有服务器线程关闭套接字让客户端知道网页已完成。

所以我的问题是,多线程 HTTP 服务器如何处理这种情况?让多个线程在同一个套接字上进行通信是严格禁忌还是不建议?现代网络浏览器对页面项发出多个并发(而不是顺序)请求似乎是有道理的,但服务器在单独的线程中处理这些请求而不是在一个线程中顺序处理这些请求似乎也有意义。线。每个请求是否必须以某种方式打开自己的套接字 - 也许通过在新线程中调用accept()?

这通常是通过关闭套接字的超时,还是通过事件计时器或类似的方式来解决?或者使用 HTTP v1.0 禁用持久连接更好?或者是否有另一种方法可以知道何时发送所有网页元素并关闭服务器套接字?这是套接字关闭代码,以防我错过了其他一些可以神奇地处理持久连接的 winsock2 选项:

bool ShutdownConnection
(
CONFIG      *configSP,
SOCKET sd
)
{
    if ( shutdown(sd, SD_SEND) == SOCKET_ERROR)
    {
        return false;
    }
    char readBuf[bufSize];

    while (1)
    {
        int newBytes = recv(sd, readBuf, bufSize, 0);

        if (newBytes == SOCKET_ERROR)
        {
            return false;
        }
        else
        {
            break;
        }
    }
    if (closesocket(sd) == SOCKET_ERROR)
    {
        return false;
    }
    return true;
}

最佳答案

Messing with the code I found that the problem is that the client (e.g. Chrome v33) is firing off multiple GET requests on the same port, meaning that the server is therefore starting new threads to handle these requests on the same socket.

不,没有。每个 accept() 返回一个新的套接字。

That is, the socket returned by accept() (i.e. the connection socket, not the listening socket) is then passed on to the CreateThread() user function to process the GET request.

正确,而且每次都是一个新的套接字。你已经自相矛盾了。

However, the first thread to start closes this socket when it is finished sending, meaning other threads find the socket has been closed when it comes to their turn to send.

没有。这些线程都有自己的套接字。除非你有一个编码错误,关闭了错误的套接字,或者以某种方式将它们混淆了,例如通过不正确的变量范围。

I experimented with not closing the socket

关闭套接字不是一个“实验”,而是一个要求

this worked better and loaded more of the page, but still didn't fully finish the page load, presumably since none of the server threads closed the socket to let the client know the webpage is complete.

没有。您的复制循环可能不正确,或者您可能混合了套接字,或者您可能没有完全独立的线程,或者您有一些其他编码错误。

So my question is, how is this situation meant to be handled by a multithreaded HTTP server?

您描述的情况不可能出现在正确编写的服务器中。

Is it strictly taboo or ill advised to have multiple threads communicating on the same socket?

应该是不可能的。如果有,则说明您的代码中有错误。

It seems to make sense that modern web browsers would make multiple concurrent (rather than sequential) requests for page items, but then it would also seem to make sense for efficiency for a server to also handle these requests in separate threads rather than sequentially in one thread.

它们必须同时处理它们。

Must each request somehow open its own socket - perhaps by calling accept() within the new thread?

在启动线程来处理新的入站套接字之前,您应该已经调用了 accept() 来获取新的入站套接字。这没有任何意义。

Is this usually solved via a timeout to close the socket, or via an alive timer or similar?

没有。

Or is it better to use HTTP v1.0 to disable persistent connections?

不必要且无效。

Or is there perhaps another way of knowing when all webpage elements have been sent and closing the server socket then?

你不需要知道。有多个套接字,每个套接字都应该在自己的适当时间关闭。不可能知道你在这里到底在说什么。

Here's the socket closing code, in case there is some other winsock2 option I missed that magically handles persistent connections:

您不需要读取循环或关闭。关闭它即可。这个来自 MSDN 文章(您没有正确复制)是关于在两端实现同步关闭,这在 HTTP 中不是必需的。不幸的是,它被广泛误解为所有 TCP 连接的要求。事实并非如此。

你完全找错了对象。我建议您发布一些有关评估的实际代码。

关于c++ - 在多线程 HTTP 服务器中发送后如何干净地关闭套接字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22128667/

相关文章:

c++ - 我可以像命名空间一样使用类作用域,而不是用类名作为前缀吗

c++ - 引用计数线程安全吗

c - 禁止 C 预处理器在另一个中使用宏

c - 函数 strcpy() 更改整数数组的值?

c++ - 设计 C++ 休息客户端

java - 如何最有效地添加一些信息到Java中的多线程列表?

c++ - 为什么我不能调用 'explicit C(const C&)'?

c++ - 将引用转换为 C++ 中的指针表示

c - 没有得到阿姆斯特朗数所需的输出

java - 线程 "http-8080-10"java.lang.OutOfMemoryError : Java 中的异常