Find the answer to your Linux question:
Results 1 to 4 of 4
I have a basic idea of what is causing the Segmentation Faul (essentially accessing memory that I'm not allowed to) when I try to run the program from the command ...
Enjoy an ad free experience by logging in. Not a member yet? Register.
  1. #1
    Just Joined!
    Join Date
    Dec 2011
    Location
    Central Florida
    Posts
    2

    Trying to fix a Segmentation Fault with multi-threading TCP Listener


    I have a basic idea of what is causing the Segmentation Faul (essentially accessing memory that I'm not allowed to) when I try to run the program from the command line. While debugging the code through Eclipse it works okay without complaining.

    I have a server that needs to accept streamed tcp data from N different machines. The number of allowable connections is established by the two arguments provided at runtime, which are two port numbers that establish a range that the server will be listening on. I then am creating a thread for each port within that range which then dumps any data received to the console.

    I understand that this code is hideous at best, but I'm just trying to make it work before building in the argument validation and better error handling.

    Hideous Code:
    Code:
    #include <iostream>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <unistd.h>
    
    using namespace std;
    
    // Declare a global variable that we can use to let us know we need
    // to exit this program.
    
    volatile bool KeepRunning = true;
    
    void error(const char *msg)
    {
           perror(msg);
           exit(0);
    }
    
    void *ListenForConnection(void *ptr);
    
    // Declare a structure that we can use to pass in any information
    // into the thread we are going to kick off.  If any new parameters
    // are needed simply add them to this "struct" object and they will
    // be available within the method that is called via the thread.
    
    struct tcp_connection_params
    {
           // This is the port number to use
           int tcp_port;
    };
    
    
    
    int main(int argc, char *argv[])
    {
           // Take the second parameter as our starting port number
    
           int from_port = atoi(argv[1]);
    
           // The third parameter will be our ending port number
    
           int to_port = atoi(argv[2]);
    
           // This variable will be used to help us identify how many
           // threads we will be needing to create
    
           int ThreadCount = to_port - from_port;
    
           // Create an array of threads, but we can't specify
           // how many threads we will need just yet
    
           pthread_t *Thread_Array;
    
           // Declare a tcp_connection_params object that we will use to pass in
           // our parameters when we create a new thread.
    
           struct tcp_connection_params params;
    
           // We need to base our number of threads upon how many
           // ports we were told to open
    
           Thread_Array = new pthread_t[ThreadCount + 1];
    
           // Now that we have our array of threads identified
           // we can run through a loop of that amount creating
           // new threads dedicated to listening on the specified
           // port number.
    
           for (int i=0; i <= ThreadCount; i++)
           {
                   pthread_create(&(Thread_Array[i]), NULL, &ListenForConnection, &params);
                   params.tcp_port = from_port + i;
           }
    
           // Make sure we wait for all of the threads to finish
           // working before we exit the program
    
           for (int i = 0; i <= ThreadCount; i++)
           {
                   pthread_join(Thread_Array[i], NULL);
           }
    
           return 0;
    }
    
    
    
    
    void *ListenForConnection(void* parameters)
    {
           // This will be used to open the original socket on the
           // designated port.
    
           int socket_file_descriptor;
    
           // At which point we will move that connection over to another
           // port which will be saved in this variable.
    
           int new_socket_file_descriptor;
    
           // This variable will be used to count the number of characters
           // that are sent from the remote system.
    
           int n;
    
           // This is an easier way for us to identify which port we will
           // be using to bind to the socket.
    
           int port_number;
    
           // We need to create a boolean value stored as an integer so
           // that we can reuse a socket once communication has finished.
    
           int yes = 1;
    
           struct sockaddr_in server_address;
           struct sockaddr_in client_address;
    
           // Here is where we will store our message
    
           char buffer[256];
    
           // create a pointer to a tcp_connection_params object so that
           // we can reference any parameters that we passed into this
           // method.
    
           struct tcp_connection_params* pp = (struct tcp_connection_params*) parameters;
    
           port_number = pp->tcp_port;
    
           socklen_t client_length;
    
           socket_file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
    
           if (socket_file_descriptor < 0)
           {
                   error("Error Opening Socket");
           }
           else
           {
                   // Make sure that we can reuse the sockets once the filler
                   // has closed the connection.
    
                   setsockopt(socket_file_descriptor, SOL_SOCKET, SO_REUSEADDR, &yes,
    sizeof(int));
    
                   // Clear the buffer used for the server's address
    
                   bzero((char *) &server_address, sizeof(server_address));
    
                   // Now that we have cleared that buffer we can populate
                   // it with our new data
    
                   server_address.sin_family = AF_INET;
                   server_address.sin_addr.s_addr = INADDR_ANY;
                   server_address.sin_port = htons(port_number);
    
                   if (bind(socket_file_descriptor, (struct sockaddr *)
    &server_address, sizeof(server_address)) < 0)
                   {
                           char* p;
                           string Crap("ERROR binding to socket on port number:" + port_number);
                           p = new char[Crap.size() + 1];
                           strcpy(p, Crap.c_str());
                           error("ERROR binding to socket");
                   }
                   else
                   {
                           listen(socket_file_descriptor, 5);
                           client_length = sizeof(client_address);
    
                           while(KeepRunning)
                           {
                                   setsockopt(new_socket_file_descriptor, SOL_SOCKET, SO_REUSEADDR,
    &yes, sizeof(int));
                                   new_socket_file_descriptor = accept(socket_file_descriptor,
    (struct sockaddr *) & client_address, &client_length);
    
                                   n = read(new_socket_file_descriptor, buffer, 255);
    
                                   if (n > 0)
                                   {
                                           if (buffer[0] == 'A')
                                                   KeepRunning = false;
                                           printf(buffer);
                                   }
    
                                   close(new_socket_file_descriptor);
    
                           }
    
                   }
                   close(socket_file_descriptor);
           }
           close(new_socket_file_descriptor);
           close(socket_file_descriptor);
           return NULL;
    }
    I'm wondering if the Segmentation Fault is due to me not allocating and de-allocating memory for the threads. Would that cause my error?

    Thanks in advance.

  2. #2
    Administrator MikeTbob's Avatar
    Join Date
    Apr 2006
    Location
    Texas
    Posts
    7,864
    Welcome to the forums.
    I don't have an answer for you but I can give your thread a little bump back to the top. Hopefully someone will come along soon and help you out.
    I do not respond to private messages asking for Linux help, Please keep it on the forums only.
    All new users please read this.** Forum FAQS. ** Adopt an unanswered post.

    I'd rather be lost at the lake than found at home.

  3. #3
    Linux Guru Cabhan's Avatar
    Join Date
    Jan 2005
    Location
    Seattle, WA, USA
    Posts
    3,252
    So, there are a few standard approaches to debugging segfaults. This is my preferred first step:

    1) Run the command "ulimit -c unlimited". This will set programs to create core dump files when they crash.
    2) Recompile your program with the "-g" option. This will add debugging symbols that gdb understands.
    3) Run the program. When it crashes, a "core" file should appear in the directory.
    4) Run the command "gdb ./CORE_FILE"

    This will launch the gdb debugger using that core file. gdb is a super complex program which I highly advise you learn, but for now, you can use the "bt" (backtrace) command to see the call stack leading to the point that the program crashed. This will show you exactly what led to the segfault, with line numbers and everything.

    Use this to start your debugging.

    Have fun!

  4. #4
    Just Joined!
    Join Date
    Dec 2011
    Location
    Central Florida
    Posts
    2
    Quote Originally Posted by MikeTbob View Post
    Welcome to the forums.
    I don't have an answer for you but I can give your thread a little bump back to the top. Hopefully someone will come along soon and help you out.
    Thanks for the bump Tbob, and thanks for the reply Cabhan.

    After much head banging and frustration I seemed to have muscled through and found a solution. I highly doubt that it is a preferred method nor is it programmatically sound, but it works. I tried to make sure and add pelty of comments explaining why I did it the way I did. I may have put in too many, but I doubt any programmers coming behind me will mind.

    In short, I think the problem came in that I was using a single variable to pass in the port number that I was trying to open, and the threaded portion was using the memory address which wasn't always the value that I thought it was just due to the natural behavior of threads. I ended up creating an array of port numbers to use and passed that via a parameter struct into the thread's method. If anyone happens to go through and finds any errors, please let me know. Especially if I have created a black-hole of a memory leak, my company will thank you.

    Here is the updated code:

    Code:
    //============================================================================
    // Name        : ThreadedListener.cpp
    // Author      : arghanoah
    // Version     : 1.0
    // Copyright   : Your copyright notice
    // Description : This program takes in two arguments to establish
    //               a port range of which a thread will be created
    //               dedicated to each port listening for any incoming
    //               TCP/IP data.
    //============================================================================
    
    #include <errno.h>
    #include <iostream>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <unistd.h>
    
    using namespace std;
    
    // Declare a global variable that we can use to let us know we need
    // to exit this program.
    
    volatile bool KeepRunning = true;
    
    void error(const char *msg)
    {
    	perror(msg);
    	exit(0);
    }
    
    void *ListenForConnection(void *ptr);
    
    // Declare a structure that we can use to pass in any information
    // into the thread we are going to kick off.  If any new parameters
    // are needed simply add them to this "struct" object and they will
    // be available within the method that is called via the thread.
    
    struct tcp_connection_params
    {
    	// This is the port number to use
    	int tcp_port;
    };
    
    
    
    int main(int argc, char *argv[])
    {
    	int from_port;
    	int to_port;
    
    	// First step is to make sure that the user entered what they
    	// were supposed to.  There should be a total of 3 arguments
    	// provided.
    	// 1.  Program Name
    	// 2.  Beginning Port Number
    	// 3.  Ending Port Number
    
    	if (argc < 3)
    	{
    		// It is allowed to have only supply two arguments, which
    		// would be the command name and a single port from which
    		// we will use to populate the "to port".  In laymans terms
    		// if the argument count is fewer than 3 AND the count is
    		// not equal to 2 we should error.
    
    		if (argc != 2)
    		{
    			error("Usage is <program_name> <starting_port> <ending_port>");
    		}
    	}
    
    	// At this point we know we at least have the minimum amount
    	// of arguments needed.  Now we will see if there are too many.
    
    	if (argc > 3)
    	{
    		error("Too many arguments provided");
    	}
    
    	// Once code execution has reached this point we know that we have the
    	// correct number of arguments.  Now we need to validate the port numbers
    	// to make sure that they won't blow anything up.
    
    	from_port = atoi(argv[1]);
    
    	if (argc == 2)
    	{
    		to_port = atoi(argv[1]);
    	}
    	else
    	{
    		to_port = atoi(argv[2]);
    	}
    
    	// Congrats, the user entered the correct number of
    	// arguments, BUT, did they enter ones that we can
    	// actually use?
    
    	if (to_port > from_port)
    	{
    		error("Starting port number must be less than or equal to Ending port number");
    	}
    
    	// Well, well, throw a party.  The user entered the correct number
    	// of arguments AND they put them in the correct order.
    	//
    	// Let's proceed shall we.....
    	//
    	// This variable will be used to help us identify how many
    	// threads we will be needing to create
    
    	int ThreadCount = to_port - from_port;
    
    	// Create an array of threads, but we can't specify
    	// how many threads we will need just yet, so we
    	// will just declare a pointer of the correct data type
    
    	pthread_t *Thread_Array;
    
    	// Declare a tcp_connection_params object array pointer
    	// that we will use to pass in our parameters when we
    	//create a new thread.
    
    	struct tcp_connection_params *Params;
    
    	// We need to base our number of threads upon how many
    	// ports we were told to open
    
    	Thread_Array = new pthread_t[ThreadCount + 1];
    	Params = new tcp_connection_params[ThreadCount + 1];
    
    	// Now that we have our array of threads and parameters
    	// identified we can run through a loop of that amount
    	// creating new threads with their associated parameters
    	// dedicated to listening on the specified port number.
    
    	for (int i=0; i <= ThreadCount; i++)
    	{
    		Params[i].tcp_port = from_port + i;
    		pthread_create(&(Thread_Array[i]), NULL, &ListenForConnection, &Params[i]);
    		pthread_detach(Thread_Array[i]);
    	}
    
    	// Make sure we wait for all of the threads to finish
    	// working before we exit the program
    
    	pthread_exit(NULL);
    
    	// Make sure that we free up any memory that was allocated
    	// for our array objects
    
    	free(Thread_Array);
    	free(Params);
    
    	return 0;
    }
    
    
    
    /*	Here is the method that is invoked each time we
    	create a new thread.  It simple will create a 
    	couple of sockets and listen on the passed in
    	port number for any incoming connections.  */	
    
    
    
    void *ListenForConnection(void* parameters)
    {
    	// This will be used to open our socket on the
    	// designated port.
    
    	int socket_file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
    
    	// Once that connection is established we will move it off
    	// to another socket to free the original up for any other
    	// connections that may try to access it.  For this particular
    	// case that I am designing this program we will only be having
    	// a single machine accessing this program on a single port.
    
    	int new_socket_file_descriptor = socket(AF_INET, SOCK_STREAM, 0);
    
    	// This variable will be used to count the number of characters
    	// that are sent from the remote system.
    
    	int characters_read;
    
    	// This is an easier way for us to identify which port we will
    	// be using to bind to the socket.
    
    	int port_number;
    
    	// We need to create a boolean value stored as an integer so
    	// that we can reuse a socket once communication has finished.
    
    	int yes = 1;
    
    	// Declare a couple of sockaddr_in objects to define
    	// the server and client when they connect	
    
    	struct sockaddr_in server_address;
    	struct sockaddr_in client_address;
    
    	socklen_t client_length = sizeof(client_address);
    
    	// Here is where we will store our message
    
    	char buffer[256];
    
    	// create a pointer to a tcp_connection_params object so that
    	// we can reference any parameters that we passed into this
    	// method.  I chose to pass in a struct so that if any new parameters
    	// need to be added at a later date I just need to define them in the
    	// struct which would prevent me from having to come downstream and 
    	// define them at other points in the program.
    
    	struct tcp_connection_params* pp = (struct tcp_connection_params*) parameters;
    
    	// Set our port number that we are going to open
    
    	port_number = pp->tcp_port;
    
    
    	if (socket_file_descriptor < 0)
    	{
    		error("Error Opening Socket");
    	}
    	else
    	{
    		// Make sure that we can reuse the sockets once the remote
    		// machine has closed the connection.	
    
    		setsockopt(socket_file_descriptor, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
    
    		// Clear the buffer used for the server's address
    
    		bzero((char *) &server_address, sizeof(server_address));
    
    		// Now that we have cleared that buffer we can populate
    		// it with our new data
    
    		server_address.sin_family = AF_INET;
    		server_address.sin_addr.s_addr = INADDR_ANY;
    		server_address.sin_port = htons(port_number);
    
    		if (bind(socket_file_descriptor, (struct sockaddr *) &server_address, sizeof(server_address)) < 0)
    		{
    			cout << "Did not open port: " << port_number << endl;
    		}
    		else
    		{
    			listen(socket_file_descriptor, 5);
    			client_length = sizeof(client_address);
    
    			while(KeepRunning)
    			{
    				setsockopt(new_socket_file_descriptor, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
    				new_socket_file_descriptor = accept(socket_file_descriptor, (struct sockaddr *) &client_address, &client_length);
    
    				bzero(buffer, 256);
    				n = read(new_socket_file_descriptor, buffer, 255);
    
    				if (n > 0)
    				{
    					if (buffer[0] == 'A')
    					{
    						KeepRunning = false;
    						close(new_socket_file_descriptor);
    						close(socket_file_descriptor);
    					}
    					cout << buffer << endl;
    				}
    
    				// Make sure that we close the "new" socket that
    				// we dumped the original connection on so that
    				// we can repeat those same steps for each
    				// successive connection.
    
    				close(new_socket_file_descriptor);
    			}
    		pthread_exit(NULL);
    		}
    	}
    	close(socket_file_descriptor);
    	return NULL;
    }

    Thanks again all for checking/helping me out with this.

    Args
    Last edited by arghanoah; 12-18-2011 at 02:44 PM. Reason: Grammar, and being anal about what I post... :)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •