Menu

#37 gsoap SOAP_IO_KEEPALIVE possible bug.

closed
nobody
None
5
2005-03-28
2004-06-29
Anonymous
No

I have tried to use the SOAP_IO_KEEPALIVE flag to send
up to 1000000 char values from a client to a server.
The behaviour is strange. Some times the client gets a
"broken pipe" and often the soap client error is
"TCP socket failed in tcp_connect()".

It Seems from the code that the "soap__ns_send...()"
calls the tcp_connect many times when accordig to the
the keep alive
flag it should call it once.

Here are the client and server interesting pieces code
(you can use it for testing pourposes), also with the
test.h header.

Client :
--------------------------------------------------------------------------------------------------
soap_init2(&soap, SOAP_IO_KEEPALIVE, SOAP_IO_KEEPALIVE);
for(int i=0; i<1000000; i++) {

char c='\0';
if (!res = soap_send_ns__sendChar(&soap, server,
NULL, c)) {
soap_print_fault(&soap, stdout);
exit(1);
}

if(res!=SOAP_OK) {
soap_print_fault(&soap, stdout);
exit(1);
}
if (soap.error) soap_print_fault(&soap, stdout);
}
--------------------------------------------------------------------------------------------------

Server:
--------------------------------------------------------------------------------------------------
soap_init2(&soap, SOAP_IO_CHUNK|SOAP_IO_KEEPALIVE,
SOAP_IO_CHUNK|SOAP_IO_KEEPALIVE);
soap.accept_timeout = TIMEOUT;
soap.bind_flags |= SO_REUSEADDR;

m = soap_bind(&soap, NULL, port, BACKLOG);
if (m < 0) {
soap_print_fault(&soap, stderr);
exit(1);
}
for (;;) {
soap_accept(&soap);
cerr << ":DONE" << endl;

for (int i=0, times=0; i<atoi(argv[2]); i++, times++){
res = soap_recv_ns__sendChar(&soap, &c);
if (res == SOAP_OK) {
cerr << "Received message " << times << endl ;
} else {
soap_print_fault(&soap, stderr);
exit(1);
}
}
cerr << "exiting" << endl;
}
--------------------------------------------------------------------------------------------------

test.h :
--------------------------------------------------------------------------------------------------

//gsoap ns service name: test
//gsoap ns service style: rpc
//gsoap ns service encoding: encoded
//gsoap ns service location: http://c6:18000
//gsoap ns schema namespace: urn:test

int ns__sendChar(assist_char c, void);
--------------------------------------------------------------------------------------------------

Thanks for interest.

Discussion

  • sbarnoud

    sbarnoud - 2004-06-30

    Logged In: YES
    user_id=1063105

    To my mind, you have exactelly the same problem that i
    described in the case 972535.

    You can try my patch :
    -) create a the following soap_poll2 fonction in stdsoap2.c[pp]:
    #ifndef MAC_CARBON
    #ifndef PALM_1
    SOAP_FMAC1
    int
    SOAP_FMAC2
    soap_poll2(struct soap * soap)
    {
    #ifndef WITH_LEAN
    struct timeval timeout;
    fd_set sfd,rfd;
    int r;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    FD_ZERO(&rfd);
    FD_ZERO(&sfd);
    if (soap->socket >= 0)
    { FD_SET(soap->socket, &rfd);
    FD_SET(soap->socket, &sfd);
    r = select(soap->socket + 1, &rfd, &sfd, NULL, &timeout);
    }
    else if (soap->master >= 0)
    { FD_SET(soap->master, &rfd);
    r = select(soap->master + 1, &rfd, &sfd, NULL, &timeout);
    }
    else
    { FD_SET(soap->sendfd, &sfd);
    FD_SET(soap->recvfd, &rfd);
    r = select((soap->sendfd > soap->recvfd ? soap->sendfd :
    soap->recvfd) + 1, &rfd, &sfd, NULL, &timeout);
    }
    if (r > 0)
    {
    #ifdef WITH_OPENSSL
    if (soap->ssl)
    { if ((soap->socket >= 0) && FD_ISSET(soap->socket, &rfd))
    { char buf = '\0';
    if (SSL_peek(soap->ssl, &buf, 1) <= 0)
    return SOAP_EOF;
    }
    }
    #endif
    if ((soap->socket >= 0 && FD_ISSET(soap->socket, &rfd))
    || (soap->recvfd >= 0 && FD_ISSET(soap->recvfd, &rfd)) ) {
    // On ne devrait rien avoir a lire , si c'est le cas
    il s'agit d'un EOF
    // Il faut alors se reconnecter
    return SOAP_EOF;
    }

    return SOAP_OK;
    }
    if (r < 0 && (soap_valid_socket(soap->master) ||
    soap_valid_socket(soap->socket)) && soap_socket_errno !=
    SOAP_EINTR)
    { soap->errnum = soap_socket_errno;
    soap_set_receiver_error(soap, tcp_error(soap), "select
    failed in soap_poll()", SOAP_TCP_ERROR);
    return soap->error = SOAP_TCP_ERROR;
    }
    else
    soap->errnum = soap_errno;
    return SOAP_EOF;
    #else
    return SOAP_OK;
    #endif
    }
    #endif
    #endif

    -) modify the soap_connect_command() function to call
    soap_poll2 and
    to have keepalive again for the new connection :

    if (*soap->host)
    { soap->status = http_command;
    if (!soap_valid_socket(soap->socket) ||
    strcmp(soap->host, host) || soap->port != port)
    { soap->keep_alive = 0; /* force close */
    soap_closesock(soap);
    DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Connect to
    host='%s' path='%s' port=%d\n", soap->host, soap->path,
    soap->port));
    soap->socket = soap->fopen(soap,
    endpoint,soap->host, soap->port);
    if (soap->error)
    return soap->error;
    soap->keep_alive = ((soap->omode &
    SOAP_IO_KEEPALIVE) != 0);
    }
    // call soap_poll2 instead of soap_poll
    else if (!soap->keep_alive || soap_poll2(soap))
    { soap->keep_alive = 0; /* force close */
    soap_closesock(soap);
    DBGLOG(TEST,SOAP_MESSAGE(fdebug, "Reconnect to
    host='%s' path='%s' port=%d\n", soap->host, soap->path,
    soap->port));
    soap->socket = soap->fopen(soap, endpoint,
    soap->host, soap->port);
    if (soap->error)
    return soap->error;
    // Set keepalive again
    soap->keep_alive = ((soap->omode &
    SOAP_IO_KEEPALIVE) != 0);
    }
    }

     
  • sbarnoud

    sbarnoud - 2004-07-06

    Logged In: YES
    user_id=1063105

    May i have your feedback ?

    Thanks.

     
  • Nobody/Anonymous

    Logged In: NO

    Sorry for answering so late.

    Your patch seems to work but i had to write a different
    server code that
    calls the "accept" more times. infact sfter some packets the
    server seems to get a "connection timesd out".
    The problem still remains in the client that instead of
    opening only one socket (as the IO_KEEP_ALIVE should do)
    it opens many sockets. (if i send 10000000 packets i get a
    socket connect refused because all ports are in use).

    Here is the interesting part of the Server code :

    for (;;) {
    done = 0;
    soap_accept(&soap);
    fprintf(stderr, "Connect :: Again\n %d", soap.recvfd);

    while(!done) {
    struct ns__sendChar msg;
    res = soap_recv_ns__sendChar(&soap, &msg);

    if (res == SOAP_OK) {
    fprintf(stderr, "Received message %d : %d\n", msg.c.item,
    msg.c.data);

    if (msg.c.item == (streamlen-1)) {
    done=1;
    }
    } else {
    soap_print_fault(&soap, stderr);
    break;
    }
    }
    }

    Yhe client still remain the same.

    Than you for you time.

     
  • Andrea Paternesi

    Logged In: YES
    user_id=918756

    Sorry for answering so late.

    Your patch seems to work but i had to write a different
    server code that
    calls the "accept" more times. infact sfter some packets the
    server seems to get a "connection timesd out".
    The problem still remains in the client that instead of
    opening only one socket (as the IO_KEEP_ALIVE should do)
    it opens many sockets. (if i send 10000000 packets i get a
    socket connect refused because all ports are in use).

    Here is the interesting part of the Server code :

    for (;;) {
    done = 0;
    soap_accept(&soap);
    fprintf(stderr, "Connect :: Again\n %d", soap.recvfd);

    while(!done) {
    struct ns__sendChar msg;
    res = soap_recv_ns__sendChar(&soap, &msg);

    if (res == SOAP_OK) {
    fprintf(stderr, "Received message %d : %d\n", msg.c.item,
    msg.c.data);

    if (msg.c.item == (streamlen-1)) {
    done=1;
    }
    } else {
    soap_print_fault(&soap, stderr);
    break;
    }
    }
    }

    Yhe client still remain the same.

    Than you for you time.

     
  • sbarnoud

    sbarnoud - 2004-07-07

    Logged In: YES
    user_id=1063105

    Hi

    I have tested my patch only for the client side (I am using
    a GLUE server).
    In my case, the client build with GSOAP 2.6 uses only 1
    socket until the server shutdown the connection.
    In that case, my patch makes that the client reconnects
    automatically instead of failing in the first request that
    follows.

    So I am wondering that your client uses hundred's of socket.
    Did you select http version 1.1 ?

    With your test program, how many times did you have a
    "Connect :: Again" messages ?

    If you have the strace (Linux) or truss (Solaris, ...)
    command could you have a look on your client and server
    process to see who close the socket ?

    Note : In my client, i use a connect, send and recv timeout
    different than 0 (the default is 0 which means no timeout)

     
  • Nobody/Anonymous

    Logged In: NO

    Hi again.

    Peraphs we have found the issue.
    We achieved to have working client and server without
    applying your patch.
    The point is that in the soap_poll() function the select
    goes in timeout.
    We just raised the time out value (for example 2 seconds)
    and all things
    began to work.

    Let me know if this work also for your problem.

    Regards.

     
  • sbarnoud

    sbarnoud - 2004-07-15

    Logged In: YES
    user_id=1063105

    Just one thing on my patch. It is tested only for the client
    (because i don't use the server) and it may have side effect
    on the server.

    However, if you now run again your test as follow :
    send 10 request, sleep enough time to have your server that
    shutdown the socket (or stop/start the server) send 2 more
    request.
    If you do that, you may see (i have that on my computer)
    that the first request after the stop/start fails.
    Then, if you apply my patch ONLY for the client and do again
    the same test, everything will be OK.

     
  • Robert van Engelen

    Logged In: YES
    user_id=354274

    The gSOAP server implements a fairness policy that limits the length of
    the keep-alive session to 100 calls, thereby forcing clients to reconnect.
    The soap->max_keep_alive value can be changed after soap_init().

    The fairness policy forbids clients to gain exclusive access to the server.
    Note that all web servers limit the keep alive session's length.

    Setting soap->max_keep_alive resolves this issue.

    In addition, the soap_poll() algorithm will be modified for gSOAP 2.7.1.
    I'd be happy to receive comments when you have a chance to look at
    gSOAP 2.7.1 to be released by the end of March 2005.

     
  • Robert van Engelen

    • status: open --> closed
     

Log in to post a comment.

MongoDB Logo MongoDB