我有一个运行启用了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的连接,这种连接在大多数时间都有效,但是很少崩溃条件(竞争条件可能),所以这对我来说没有解决方案。
答案 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);
请注意以下事项:
但是我们还没有完成,感谢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;