GSOAP服务卡住?
程序员文章站
2022-07-14 17:05:54
...
很久以前参考了https://www.genivia.com/doc/soapdoc2.html 中的一段:
How to Create a Multi-Threaded Stand-Alone Service
完成了一个简单多线程服务器的编写。
但是一直以来服务器运行一段时间,接收一定量的请求后,就会出现服务器再也不返回的情况。
怀疑过是不是socket数量不够用了,后来跟踪发现还能正常listen。
怀疑是不是工作线程异常退出了,补充了所需的日志,也没有。
后来查询到,有人曾经也问过这个问题:
https://sourceforge.net/p/gsoap2/bugs/844/
在gsoap的代码中加入日志,确实是在soap_serve卡住了,而卡住的位置就是在recv函数,位于文件stdsoap2.cpp的933行(72行):
frecv(struct soap *soap, char *s, size_t n)
{ register int r;
register int retries = 100; /* max 100 retries with non-blocking sockets */
soap->errnum = 0;
#if defined(__cplusplus) && !defined(WITH_LEAN) && !defined(WITH_COMPAT)
if (soap->is)
{ if (soap->is->good())
return soap->is->read(s, (std::streamsize)n).gcount();
return 0;
}
#endif
if (soap_valid_socket(soap->socket))
{ for (;;)
{
#ifdef WITH_OPENSSL
register int err = 0;
#endif
#ifdef WITH_OPENSSL
if (soap->recv_timeout && !soap->ssl) /* SSL: sockets are nonblocking */
#else
if (soap->recv_timeout)
#endif
{ for (;;)
{ r = tcp_select(soap, soap->socket, SOAP_TCP_SELECT_RCV | SOAP_TCP_SELECT_ERR, soap->recv_timeout);
if (r > 0)
break;
if (!r)
return 0;
r = soap->errnum;
if (r != SOAP_EINTR && r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK)
return 0;
}
}
#ifdef WITH_OPENSSL
if (soap->ssl)
{ r = SSL_read(soap->ssl, s, (int)n);
if (r > 0)
return (size_t)r;
err = SSL_get_error(soap->ssl, r);
if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
return 0;
}
else if (soap->bio)
{ r = BIO_read(soap->bio, s, (int)n);
if (r > 0)
return (size_t)r;
return 0;
}
else
#endif
#ifdef WITH_GNUTLS
if (soap->session)
{ r = (int)gnutls_record_recv(soap->session, s, n);
if (r >= 0)
return (size_t)r;
}
else
#endif
{
#ifndef WITH_LEAN
if ((soap->omode & SOAP_IO_UDP))
{ SOAP_SOCKLEN_T k = (SOAP_SOCKLEN_T)sizeof(soap->peer);
memset((void*)&soap->peer, 0, sizeof(soap->peer));
r = recvfrom(soap->socket, s, (SOAP_WINSOCKINT)n, soap->socket_flags, (struct sockaddr*)&soap->peer, &k); /* portability note: see SOAP_SOCKLEN_T definition in stdsoap2.h */
soap->peerlen = (size_t)k;
#ifndef WITH_IPV6
soap->ip = ntohl(soap->peer.sin_addr.s_addr);
#endif
}
else
#endif
r = recv(soap->socket, s, (int)n, soap->socket_flags);
#ifdef PALM
/* CycleSyncDisplay(curStatusMsg); */
#endif
if (r >= 0)
return (size_t)r;
r = soap_socket_errno(soap->socket);
if (r != SOAP_EINTR && r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK)
{ soap->errnum = r;
return 0;
}
}
#if defined(WITH_OPENSSL)
if (soap->ssl && err == SSL_ERROR_WANT_WRITE)
r = tcp_select(soap, soap->socket, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, soap->recv_timeout ? soap->recv_timeout : 5);
else
#elif defined(WITH_GNUTLS)
if (soap->session && gnutls_record_get_direction(soap->session))
r = tcp_select(soap, soap->socket, SOAP_TCP_SELECT_SND | SOAP_TCP_SELECT_ERR, soap->recv_timeout ? soap->recv_timeout : 5);
else
#endif
r = tcp_select(soap, soap->socket, SOAP_TCP_SELECT_RCV | SOAP_TCP_SELECT_ERR, soap->recv_timeout ? soap->recv_timeout : 5);
if (!r && soap->recv_timeout)
return 0;
if (r < 0)
{ r = soap->errnum;
if (r != SOAP_EINTR && r != SOAP_EAGAIN && r != SOAP_EWOULDBLOCK)
return 0;
}
if (retries-- <= 0)
return 0;
#ifdef PALM
r = soap_socket_errno(soap->socket);
if (r != SOAP_EINTR && retries-- <= 0)
{ soap->errnum = r;
return 0;
}
#endif
}
}
#ifdef WITH_FASTCGI
return fread(s, 1, n, stdin);
#else
#ifdef UNDER_CE
return fread(s, 1, n, soap->recvfd);
#else
#ifdef WMW_RPM_IO
if (soap->rpmreqid)
r = httpBlockRead(soap->rpmreqid, s, n);
else
#endif
#ifdef WIN32
r = _read(soap->recvfd, s, (unsigned int)n);
#else
r = read(soap->recvfd, s, (unsigned int)n);
#endif
if (r >= 0)
return (size_t)r;
soap->errnum = soap_errno;
return 0;
#endif
#endif
}
按照上面的网址链接,解决此问题的办法是:
You have to set the recv_timeout and send_timeout values as well. I/O may block otherwise
更神奇的是在这个文档中:https://www.genivia.com/tutorials.html
例子里面的soap则添加了超时处理!
int main()
{
struct soap *soap = soap_new1(SOAP_IO_KEEPALIVE); /* new context with HTTP keep-alive enabled */
SOAP_SOCKET m; /* master socket */
THREAD_TYPE tids[POOLSIZE]; /* thread pool */
int i;
soap->accept_timeout = 24*60*60; /* quit after 24h of inactivity (optional) */
soap->send_timeout = soap->recv_timeout = 5; /* 5 sec socket idle timeout */
soap->transfer_timeout = 10; /* 10 sec message transfer timeout */
我就想问官方,你的例子为啥不能一起更新呢?上一篇: android之handler使用