Thursday 15 December 2011

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

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.

No comments:

Post a Comment