Working on an "opportunity" at work where I need a multi-threaded application to listen for N connections coming in. I am getting a
"Segmentation Fault" error when I try to run the program from the command line, but when debugging it executes exactly like it should be.
I'm basically stuck at lik 98% completion on this project and frankly I'm tired of beating my head against a wall to get it to work.
Since this request is a REAL reach I won't post the code up unless anyone is stup.... ahem.... brave enough to offer help.
Thanks,
Beags
Beags, my in-house techie (aka No2 son) has done some Linux development and asks for more info such as:-
First what programming language?
When does it segment fault - straight away?
What distribution are you using?
Are you calling any external progs. or libraries?
Anything else you think can help as he's saying it could be numerous causes?
Maybe best if the two of you correspond by email or skype (he's around today) - if you want to talk to him let me have yours by U2U.
Cheers, Pewe
quote:
Originally posted by pewe
Beags, my in-house techie (aka No2 son) has done some Linux development and asks for more info such as:-
First what programming language?
When does it segment fault - straight away?
What distribution are you using?
Are you calling any external progs. or libraries?
Anything else you think can help as he's saying it could be numerous causes?
Maybe best if the two of you correspond by email or skype (he's around today) - if you want to talk to him let me have yours by U2U.
Cheers, Pewe
Beags, he's just gone off to Post Office but says he'll contact you on his return.
Cheers, Pewe.
Try running a dtrace on it :
dtrace -Ff yourprogram 2> trace.log
You can then look in trace.log to try and find where it seg faulted and work your way back up the trace looking for errors.
Andy
Have you tried the "back to basics" debugging; lots of printf statements so you can find out how far execution progresses before the
segfault?
Are you dynamically allocating any memory early on in in your code? I had something similar years back that took ages to find, but was obvious in
retrospect:
something like:
buffer = malloc( length + 1 * sizeof(int) )
When I really meant
buffer = malloc( ( length + 1 ) * sizeof(int) )
A Linux guru at work says it's worth trying Valgrind
quote:
Originally posted by MikeRJ
Have you tried the "back to basics" debugging; lots of printf statements so you can find out how far execution progresses before the segfault?
Are you dynamically allocating any memory early on in in your code? I had something similar years back that took ages to find, but was obvious in retrospect:
something like:
buffer = malloc( length + 1 * sizeof(int) )
When I really meant
buffer = malloc( ( length + 1 ) * sizeof(int) )
A Linux guru at work says it's worth trying Valgrind
Try lsof to look for processes hanging on to ports.
Thanks for the help all, I *think* that I finally got it figured out. It runs without any problems, I'm just still not sure if its done the
"correct" way or if I just brute forced it. Anyway, I'll post the code up I guess in case anyone is remotely interested.
Thanks again!!! Here is the updated code:
[CODE]
//============================================================================
// Name : ThreadedListener.cpp
// Author : beagley
// 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.tcp_port = from_port + i;
pthread_create(&(Thread_Array), NULL, &ListenForConnection, &Params);
pthread_detach(Thread_Array);
}
// 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;
}
[/CODE]
Again, a million thanks!!
Beags
Going back to basics, do you have to implement it in C++?
quote:
Originally posted by scudderfish
Going back to basics, do you have to implement it in C++?
quote:
Originally posted by beagley
[CODE]
...
...
// 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;
}
[/CODE]
From the documentation I found Here
"To allow other threads to continue execution, the main thread should terminate by calling pthread_exit() rather than exit(3)."
I haven't encountered ay issues with it so far. I ran a test all weekend sending 250,000 records and i didn't lose a single one and the
program was able to exit normally this morning when I got into work.
Anyway.....
Thanks again!
quote:
Originally posted by MikeRJ
quote:
Originally posted by beagley
[CODE]
...
...
// 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;
}
[/CODE]
I'm probably missing something, but surely pthread_exit() in main will cause main to exit before the Thread_Array and Params structures are freed?
[Edited on 19/12/11 by MikeRJ]
After going over the code some more I think you guys are correct. Pthread_exit() is the wrong method to be calling. Thankfully due to poor design
it's not being called at the moment. This was a quick and dirty fix for some communication problems we've been having at my facility.
Thanks for providing all of the extra eye on this code, really helps to have that.
Beags
Incidentally, did you have a look at Mono (http://www.mono-project.com/Main_Page), and implementation of C# on things other than Windows?