3

How come when a thread exits the parent process also exits? When I run the server all is well. It sits and listens on the socket. When a client connects the server threads to serve it. When they talk back and forth the client exits and the server quits as well. I"m using pthread.h for the threading. Here they are!

First the client:

#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>

#define CLIENT_CONNECTED 0
#define CLIENT_NOT_CONNECTED 1
#define PORT 9999
#define MAX_CLIENTS 100

using namespace std;

struct client {
    int socket;
    int state;
    pthread_t tid;
};

int 
connectToServer (char *address )
{
    struct hostent *hostinfo;
    struct sockaddr_in name;
    int s;  
    int rc = 0;

    if ( ( s = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
    {
        cerr<<"Client could not declare socket"<<"\n";
        exit( 1 );
    }

    name.sin_family = AF_INET;
    name.sin_port = htons ( ( unsigned short int ) PORT );
    name.sin_addr.s_addr = htonl( INADDR_ANY );
    hostinfo = gethostbyname ( address );

    if ( hostinfo == NULL )
    {
        cerr<<"Host unknown"<<"\n";
        exit( 1 );
    }   

    name.sin_addr = *( struct in_addr * ) hostinfo->h_addr;

    if ( connect( s, ( const sockaddr * ) &name, sizeof( name ) ) < 0 )
    {
        cerr<<"Could not connect to host"<<"\n";
        exit( 1 );
    }
    else 
    {
/*      if( fcntl( s, F_SETFL, O_NONBLOCK ) == -1 ) 
        {
            perror( "fcntl" );
            exit( 1 );
        } */

        char readbuf[1024];
        char message[ ] = "START";
        rc = send( s, message, strlen(message), 0 );
        cout<<"RC on send() was "<<rc<<"\n";
        if ( rc > 0 )
        {
            cout<<"using recv...\n";

            while( ( rc = recv( s, readbuf, 1, 0 ) ) > 0 )
            { 
                readbuf[ rc ] = '\0';
                cout<<"Server responds with: "<<readbuf<<"\n";
            }

            return true;
        }
        else 
        {
            return false;
        }
    }
}

void 
killsignal( int param )
{
   fprintf( stderr, "Disconnecting." );
   exit( 1 );
}

int 
main ( )
{
    signal( SIGKILL, killsignal );
    signal( SIGINT, killsignal );

    char address[] = "localhost";
    connectToServer( address );
}

And the server:

#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>

#define CLIENT_CONNECTED 0
#define CLIENT_NOT_CONNECTED 1
#define PORT 9999
#define MAX_CLIENTS 100

using namespace std;

struct client {
    int socket;
    int state;
    pthread_t tid;
};

int 
sendClient( char *message, int *socket )
{
    int rc = 0;
    cout<<message<<"\n";
    rc = send( *socket, message, strlen(message), 0 ); 
    cout<<"send RC is: "<<rc<<"\n";
}

void 
strtochrstr( char **to, string from )
{

    int len = from.size( );

    *to = ( char * )malloc( ( len + 1 ) * sizeof( char ) );

    if ( to == NULL ){
        cout<<"out of memory!\n";
        exit( 1 );
    }

    *to[ 0 ] = '\0';

    strcpy( *to, from.c_str( ) );
}

int 
createSocket ( int *s )
{
    struct sockaddr_in name;

    if ( ( *s = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
    {
        cerr<<"Server: Socket"<<"\n";
    }

    name.sin_family = AF_INET;
    name.sin_port = htons( (unsigned short int) PORT );
    name.sin_addr.s_addr = htonl( INADDR_ANY );
    if ( bind( *s, ( struct sockaddr * ) &name, sizeof( name ) ) < 0 )
    {
        cerr<<"Could not bind to socket."<<"\n";
        exit( 1 );
    }

    return *s;
}

void *
serveClient( void *clientState )
{
    int c;
    int rc = 0;
    char readbuf[ 2 ];
    char message[ ] = "Message to client";//( char * ) malloc( 300 * sizeof( char ) );
    struct client *mystate = ( struct client * ) clientState;

    /* Set socket tot NONBLOCKING */
    if( fcntl( mystate->socket , F_SETFL, O_NONBLOCK ) == -1 ) 
    {
        perror( "fcntl" );
        exit( 1 );
    } 

    while ( true )
    {
        while ( ( rc = recv( mystate->socket, readbuf, 1 , 0 ) ) > 0 )
        {
            readbuf[ 1 ] = '\0'; 
            cout<<readbuf<<"\n";
        }
        sendClient( message, &( mystate->socket ) ); 
    }
}

int 
listenUp ( int *s )
{
    int i = 0;
    int error = 0;
    pthread_t clientIds[MAX_CLIENTS];
    struct client clients[MAX_CLIENTS];
    struct sockaddr_in fsaun[MAX_CLIENTS];  
    int fromlen[MAX_CLIENTS];   

    while ( i++ < MAX_CLIENTS )
        clients[i].state = CLIENT_NOT_CONNECTED;

    if ( listen( *s, 10 ) < 0 )
    {
        cerr<<"Could not listen on socket"<<"\n";
        exit( 1 );
    }   

    while ( true )
    {
        while ( ( clients[i++].state == CLIENT_CONNECTED && i < MAX_CLIENTS ) );    

        if ( ( clients[i].socket = accept( 
            *s, 
            ( sockaddr * ) &fsaun[i],
            ( socklen_t * ) &fromlen[i] ) ) < 0 )
        {
            cerr<<"Could not accept connection "<<i<<"\n";
        }
        else 
        {
            error = pthread_create(
                &clients[i].tid,
                NULL,
                serveClient,
                (void *)&clients[i]
            );
        }
        i = 0;
    }
}

void 
killsignal( int param )
{
   fprintf( stderr, "Disconnecting.\n" );
}

void 
intsignal( int param )
{
    fprintf( stderr, "Write error.\n" );
}

int 
main ( )
{
    signal( SIGKILL, killsignal );
    signal( SIGINT, intsignal );

    int mySock = createSocket( &mySock );
    listenUp( &mySock );
}
Deanie
  • 2,316
  • 2
  • 19
  • 35
KeatsKelleher
  • 10,015
  • 4
  • 45
  • 52

3 Answers3

8

Your server is continously sending data to the client. When your client exits, it hasn't read all the data there is to read on the socket.

This condition will generate a TCP RST, the default behavior on *nixes when a TCP RST is received is to deliver the SIGPIPE signal to the process. The default behavior of the SIGPIPE signal is to exit the program.

For TCP servers its common to just ignore the SIGPIPE signal, with SIGPIPE ignored write()/send() will return an error when the mentioned condition(a client exits or closes a socket with pending data) and set errno to EPIPE

Add this in main() of your server:

signal(SIGPIPE,SIG_IGN);

Some more info can be found here

nos
  • 223,662
  • 58
  • 417
  • 506
3

exit terminates the process so including all threads you spawned inside that same process. You should just return from your thread-functions without calling exit.

Bernd Elkemann
  • 23,242
  • 4
  • 37
  • 66
  • I don't know pthreads that well, does the OP need to join on the finished threads to clean them up? – Jack Kelly Mar 05 '11 at 22:20
  • 1
    OP has 2 different programs, he is not terminating by exiting in a thread. – nos Mar 05 '11 at 22:21
  • @nos: His "server" process spawns threads. He has two processes and many threads. – CB Bailey Mar 05 '11 at 22:22
  • @ Charles Bailey and his problem is that when the client exits, it kills the server. The problem is not that when a server thread ends, it exit()'s the server – nos Mar 05 '11 at 22:23
  • I made an error. It's not exit() that kills the thread/server. My mistake. – KeatsKelleher Mar 05 '11 at 22:27
  • @nos: At least one instance of `exit` that is potentially called from a spawned thread. It's not the only (or even main) problem but @eznme is correct to point this out as a potential issue. – CB Bailey Mar 05 '11 at 22:28
  • @akellehe I think it is. In your server, in the function serveClient you call `exit(1)`. This exits your whole server, not just the client-listener-thread. – Bernd Elkemann Mar 05 '11 at 22:30
  • @Charles Bailey ,yes, there's error paths calling exit(), and a handful of other bad stuff about this code (e.g it's leaking memory evary time a thread ends as they're not detached or joined on, the struct sockaddr is not fully initalized, sendClient doesn't handle EWOULDBLOCK on a non-blocking socket, and many other things), – nos Mar 05 '11 at 22:39
2

exit() kills the whole process, which is going to kill all of the threads that comprise it. You use the words "thread" and "process" interchangeably, which suggests there might be some confusion between the two in your mind.

Multiple threads can execute within a single process, but if the process dies, all of its threads die.

Jack Kelly
  • 18,264
  • 2
  • 56
  • 81