Alastair Micallef
Alastair Micallef

Reputation: 79

C Programming multiple threads on XUbuntu

I'm trying make the program run multiple threads to it connects to different ports. I successfully made it work on a single thread but not multiple.

Below I have posted the code of what I'm using on XUbuntu.

server.c

#include <stdio.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

// File io storing in lof file
#include "server_portLog.h"
// Thread used to create sockets
#include "sockets_pthread.h"

#define BUFFER_SIZE 1024

int main(int argc, char *argv[]) {  
// Server port number
//int portNumber = atoi(argv[1]);

// sockfd: ip-address socket, newsockfd: socket from receiving     client, portNum: Which port will be listening, num_bytes: received data     from client
int sockfd, newsockfd, num_bytes;
// buffer: will send & receive values from the server
char buffer[BUFFER_SIZE];
struct sockaddr_in serv_addr, cli_addr;
socklen_t clilen = sizeof(cli_addr);

// Getting all ports from command line parameters and creating a socket for each
int numPorts = argc - 1;
struct port varPorts[numPorts];
pthread_t portsSockets[numPorts];
for (int i = 0; i < numPorts; i++) {
    varPorts[i].portNumber = atoi(argv[i + 1]);
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_create(&portsSockets[i], &attr, createSocket, &varPorts[i]);
}

// Infinite loop too keep listening even after connection to client closes
while (1) {
    // After that all the ports entered have a socket of their own the program runs them parallel together to see if any client tries to connect with one of the ports
    for (int i = 0; i <= numPorts; i++) {
        pthread_join(&portsSockets[i], NULL);
        /* Start listening for the clients (thread blocks) */
        if (listen(varPorts[i].sockfd, 5) != 0) {
            printf("Error: listen() failed for port: %d \n", varPorts[i].portNumber);
            //return 3;
        }

        // Accepting connection from client & creating socket with that client data
        newsockfd = accept(varPorts[i].sockfd, (struct sockaddr *)&cli_addr, &clilen);
        if (newsockfd < 0) {
            printf("Error: accept() failed for port: %d \n", varPorts[i].portNumber);
            //return 4;
        }

        /* To send receive data */
        // Clearing buffer
        memset(buffer, 0, BUFFER_SIZE);
    
        // Show data received from client
        num_bytes = recv(newsockfd, buffer, BUFFER_SIZE-1, 0);
        if (num_bytes < 0) {
            printf("Error: recv() failed for port: %d \n", varPorts[i].portNumber);
            //return 5;
        }

        // Checking version of server if LOGFILE it creates a file to store the ports
        #if defined LOGFILE
        // Checking if user wrote a fileName for the logs or going to use the default log file
        if (argc == 3) {
            char *textFile = argv[argc-1];
            serverLogFile_Custom(buffer, textFile);
        }
        else {
            serverLogFile_Defualt(buffer);
        }
        #else
        // Print the port numbers that connect to server
        printf("Received: Client using port- %s to connect \n", buffer);
        #endif
        // Closing connection with client
        close(newsockfd);
    }
}
return 0;
 }

Sockets_pthreads.h

   #include <pthread.h>

struct port {
int portNumber;
int sockfd;
};

 void* createSocket(void* portNumber) {
// sockfd: ip-address socket, newsockfd: socket from receiving    client, portNum: Which port will be listening, num_bytes: received data    from client
int sockfd, newsockfd, num_bytes;
// buffer: will send & receive values from the server
//char buffer[BUFFER_SIZE];
struct sockaddr_in serv_addr, cli_addr;
socklen_t clilen = sizeof(cli_addr);

struct port *portStruct = (struct port*) portNumber;
// Creating a new socket with ip-Protocol_tcp
    // Parameters: Internet-domain, socket-stream, TCP-protocol
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0) {
    printf("Error: Failed to open socket for port: %d \n", portStruct->portNumber);
    //return 1;
}

// Setting all bits in padding-field to 0 
memset(&serv_addr, 0, sizeof(serv_addr));
// Initializing socket in sockaddr_in (stucture)
serv_addr.sin_family = AF_INET; // Seting family-Internet
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(portStruct->portNumber); // Setting portNum (passed in command line)

// Binding the address-structure to the socket
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
    printf("Error: bind() failed for port: %d \n", portStruct->portNumber);
    //return 2;
}
// Geting sockfd
portStruct->sockfd = sockfd;

pthread_exit(0);
 }

Upvotes: 0

Views: 52

Answers (2)

PhoenixBlue
PhoenixBlue

Reputation: 1037

The main function should wait for connections. Upon receiving a request from the client, you create a thread that will handle this specific connection. So you will create the threads in the loop, meaning, you can theoretically have an infinite number of threads. However, you can add a little logic to limit the number of threads that are existing at a particular time (thread pool).

So your main loop can look like this:

 while (1) {
        // accept: wait for a connection request 
        childfd = accept(parentfd, (struct sockaddr *) &clientaddr, (socklen_t *) &clientlen);
        if (childfd < 0){
            fprintf(stderr,"ERROR on accept");
            continue;
        }

        hostaddrp = inet_ntoa(clientaddr.sin_addr);
        if (hostaddrp == NULL){
            fprintf(stderr,"ERROR on inet_ntoa\n");
            continue;
        }
        fprintf(stdout,"server established connection with client\n");

        pthread_t new_thread;
        newSock     = malloc(1);
        *newSock    = childfd;
        if( pthread_create( &new_thread , NULL ,  server_thread , (void*) newSock) < 0){
            bzero(logMsg, MAXSTRING);
            sprintf(logMsg, "Thread for connection %d could not be created",childfd);
            fprintf(stderr, "%s\n", logMsg);
            continue;
        }
        fprintf(stdout, "thread created for connection %d\n", childfd);
    }

The server_thread function could look like:

void *server_thread(void* clientSock){
    int                 childfd = *(int*)clientSock;
    char                buf[MAXLINE];       // message buffer
    int                 n;                  // message byte size
    char                logMsg[MAXSTRING];
    size_t              siz_failresp;

    // read: read input string from the client
    bzero(buf, MAXLINE);
    n = (int) read(childfd, buf, MAXLINE);
    if (n < 0){
        sprintf(logMsg, "ERROR reading from socket");
        fprintf(stderr,"%s", logMsg);
        close(childfd);
        fprintf(stdout, "Client %d disconnected \n=================\n", childfd);
        //Free the socket pointer
        free(clientSock);
        return NULL;
    }

    // else, do processing of data received...
    // ...................
}

There may be unused variables here above... I just got this code from one of my projects, just removing parts that do not concern you :-)

Hope it helps

Upvotes: 1

anand
anand

Reputation: 163

Problem is not clear what needs to be implemented. If various ports sockets have to accept, then it has to happen in thread function as well as recv call. In server function there are accept and recv calls which are blocking by default.

Upvotes: 1

Related Questions