在启用了http-keepalive的情况下正常关闭(多线程)gSOAP服务

时间:2011-10-05 09:18:36

标签: gsoap keep-alive

我有一个运行启用了http-keepalive的多线程gSOAP服务。当仍有客户端连接时,如何正常关闭服务?

gSoap: how to gracefully shutdown the webservice application?中提出了一个类似的问题,但答案并没有涵盖http-keepalive方面:在客户端未关闭http-keepalive-session之前,soap-serve函数将不会返回。因此,接受的答案中的步骤2将阻止,直到客户端决定关闭连接(或接收超时到期,但短暂的超时将在此处中断所需的http-keepalive行为)。

gSOAP文档中的示例遇到了同样的问题。

我到目前为止尝试的是为所有在主线程的soap_serve调用中挂起的soap结构调用soap_done()来中断等待http-keepalive的连接,这种连接在大多数时间都有效,但是很少崩溃条件(竞争条件可能),所以这对我来说没有解决方案。

2 个答案:

答案 0 :(得分:0)

我遇到了同样的问题,我想我已经找到了解决方案。

正如您刚才所说,问题是gSoap挂在soap_serve上。发生这种情况是因为gSOAP为您生成一个内部循环,等待所有保持活动请求的到达或服务器端的超时。

我所做的就是在自动生成的服务存根中抓取soap_serve函数。我将列出原始的soap_serve函数,以便您可以在服务存根文件中找到它:

  SOAP_FMAC5 int SOAP_FMAC6 soap_serve(struct soap *soap)
    {
    #ifndef WITH_FASTCGI
        unsigned int k = soap->max_keep_alive;
    #endif

        do
        {
    #ifdef WITH_FASTCGI
            if (FCGI_Accept() < 0)
            {
                soap->error = SOAP_EOF;
                return soap_send_fault(soap);
            }
    #endif

            soap_begin(soap);

    #ifndef WITH_FASTCGI
            if (soap->max_keep_alive > 0 && !--k)
                soap->keep_alive = 0;
    #endif

            if (soap_begin_recv(soap))
            {   if (soap->error < SOAP_STOP)
                {
    #ifdef WITH_FASTCGI
                    soap_send_fault(soap);
    #else 
                    return soap_send_fault(soap);
    #endif
                }
                soap_closesock(soap);

                continue;
            }

            if (soap_envelope_begin_in(soap)
             || soap_recv_header(soap)
             || soap_body_begin_in(soap)
             || soap_serve_request(soap)
             || (soap->fserveloop && soap->fserveloop(soap)))
            {
    #ifdef WITH_FASTCGI
                soap_send_fault(soap);
    #else
                return soap_send_fault(soap);
    #endif
            }

    #ifdef WITH_FASTCGI
            soap_destroy(soap);
            soap_end(soap);
        } while (1);
    #else
        } while (soap->keep_alive);
    #endif
        return SOAP_OK;
    }

你应该提取这个函数的主体并用你的代码替换你的线程内的旧soap_serve(mySoap)调用(由于keep-alive而执行请求和hagns的线程):

do
    {
        if ( Server::mustShutdown() ) {
            break;
        }

        soap_begin(mySoap);

        // If we reached the max_keep_alive we'll exit
        if (mySoap->max_keep_alive > 0 && !--k)
            mySoap->keep_alive = 0;


        if (soap_begin_recv(mySoap))
        {   if (mySoap->error < SOAP_STOP)
            {
                soap_send_fault(mySoap);
                break;
            }
            soap_closesock(mySoap);

            continue;
        }

        if (soap_envelope_begin_in(mySoap)
         || soap_recv_header(mySoap)
         || soap_body_begin_in(mySoap)
         || soap_serve_request(mySoap)
         || (mySoap->fserveloop && mParm_Soap->fserveloop(mySoap)))
        {
            soap_send_fault(mySoap);
            break;
        }


    } while (mySoap->keep_alive);

请注意以下事项:

  1. Server :: mustShutdown()充当一个标志,将设置为true(外部)以结束所有线程。当您想要停止服务器处理新请求时,此函数将返回true并且循环将结束。
  2. 我删除了ifdef,WITH_FASTCGI现在对我们来说并不感兴趣。
  3. 当您关闭此连接时,连接到服务器的任何客户端都将引发异常。例如,使用C#编写的客户端将抛出一个“服务器已关闭基础连接以保持活着”,这对我们来说非常有意义。
  4. 但是我们还没有完成,感谢AudioComplex所指出的,系统仍然在等待soap_begin_recv的请求。但我也有解决方案;)

    连接处理池中的每个线程都创建了一个主soap上下文的副本(通过soap_copy),这些线程是那些线程 我将每个上下文存储为驻留在主连接处理线程上的数组上的元素。 当终止主连接处理线程(提供请求的线程)时,它将遍历所有soap上下文并使用以下命令“手动”完成连接:

    for (int i = 0; i < soaps.size(); ++i) {
      soaps[i]->fclose(soaps[i]);
    }
    

    这将强制soap_serve循环完成。它实际上会在stdsoap2.cpp_

    的第921行附近停止内部循环
    r = select((int)soap->socket + 1, &fd, NULL, &fd, &timeout);
    

    这不是最干净的解决方案(没有找到更干净的解决方案),但肯定会停止服务。

答案 1 :(得分:0)

您不需要更改soap_serve中的循环,只需在您的服务实现中返回一些错误代码:

return Server::mustShutdown() ? SOAP_SVR_FAULT : SOAP_OK;