Skip to content
Fix Code Error

problem with client server unix domain stream sockets (servered is able to be accessed by multiple clients)

June 29, 2021 by Code Error
Posted By: Anonymous

as mention above i am trying to make a server and client program.
the client should send the length of the sequence of int and the sequence to the server. from the server it will receive the average if it is greater than 10 and a confirmation message else it will receive a failure message. The server creates threads and in the function executed by the threads it does what the client wants plus it keeps count of the successful as well as the total sequences which he receives . I have tried what follows and after i insert the array elements it does nothing and i am not sure why(as you may have guessed i am new to sockets)

server code

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define SOCK_PATH "socket"
#define SUCCESS "Sequence ok"
#define FAILURE "Check failed"
int succcounter = 0;
int totalcounter = 0;

void *average_function (void *arg)
       {
        int so = (int) arg;
        int done, n;
        int sum=0;
        int *num,m;
        float average;
        char str[100];
        done = 1 ;
            do {
                n = recv(so, str, 4, 0);
                m = (int)str;
                num = (int *)malloc(m);
                for(int i=0; i<m; i++){
                    n = recv(so, str, sizeof(num[m]), 0);
                    num[i] = (int) str[i];
                }
                for (int i=0; i<m; i++){
                    sum += num[i];
                }
                average = sum/m;
                if (average > 10){
                   send(so,(char *)&average,sizeof(average),0);
                   send(so, SUCCESS, strlen(SUCCESS), 0);
                   succcounter++;
                   totalcounter++;
                }
                else{
                    average = 0;
                    send(so,(char *)&average,sizeof(average),0);
                    send(so,FAILURE,strlen(FAILURE),0);
                    totalcounter++;
                }

                if (n==1) done = 1; 

            } while (!done);

         close(so);
         pthread_exit(NULL);
        }







int main(void)
    {
        int s, s2, i, t, len;
        struct sockaddr_un local, remote;
        pthread_t thread[50];

        if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(1);
        }

        local.sun_family = AF_UNIX;
        strcpy(local.sun_path, SOCK_PATH);
        unlink(local.sun_path);
        len = strlen(local.sun_path) + sizeof(local.sun_family);
        if (bind(s, (struct sockaddr *)&local, len) == -1) {
            perror("bind");
            exit(1);
        }

        if (listen(s, 5) == -1) {
            perror("listen");
            exit(1);
        }

        i=0;

        for(;;) {
            t = sizeof(remote);
            if ((s2 = accept(s, (struct sockaddr *)&remote, &t)) == -1) {
                perror("accept");
                exit(1);
            }
            pthread_create(&(thread[i++]), NULL, average_function, (void *)s2);
         }

        return 0;
    }

client code

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>

#define SOCKET_PATH "socket"

int main(void)
{
    int s,len,t,N = 0;
    int *arr;
    char str[100],ch;
    float avg=0;
    struct sockaddr_un remote;
    if((s = socket(AF_UNIX,SOCK_STREAM,0)) == -1) {   //check if socket was created
        perror("socket");
        exit(1);
    }
    remote.sun_family = AF_UNIX;                   //passing the values to the struct 
    strcpy(remote.sun_path,SOCKET_PATH);           //same
    len = strlen(remote.sun_path) + sizeof(remote.sun_family);
    if(connect(s,(struct sockaddr *)&remote,len) == -1){     //connetct and check
        perror("connect");
        exit(1);
    }
    do{
        printf("Give array dimension: ");
        scanf("%d",&N);
        arr = (int *)malloc(N);               //dynamicly allocating memory for the array
        for(int i=0; i<N; i++){
            printf("Give array element: ");
            scanf("%d",&arr[i]);             //populating the array
        }
        send(s,(char *)&N,sizeof(int),0);    //send(file descriptor,void *buff,size,flags) sending N
        for(int i=0; i<N; i++){
            send(s,(char *)&arr[i],sizeof(arr),0);    //sending array elements
        }                                                 
        t = recv(s,str,100,0);
        str[t] = '';
        printf("Server sent this message %sn",str);        // receive confirmation or failure
        recv(s,(char *)&avg,sizeof(float),0);            //receiving average and printing it
        if(avg != 0){
            printf("Server also sent the average which is %.2f",avg);
        }
        printf("Do you want to continue?(Y/N)");
        ch = getchar();
        free(arr);
    }
    while (ch != 'N'|| ch != 'n');
    close(s);
    return 0;
}

Solution

Server sends a variable length string (confirmation) but we need a fixed length message because client can’t know beforehand the length to read. Better to just send an int that is 0=fail, 1=success.

Server sends average first but client reads success/fail message first (i.e. a mismatch in the order of send/recv for these.

Method to terminate client connection in server is broken. Better to have client send an explicit stop message (and server has to check connection).

The server doesn’t do pthread_join so it can overrun the thread ID array. I changed that to a linked list of a [new] per-task struct. But, I couldn’t get the join code to work. Still, it should give you some idea of how to control threads in the general case, even though it’s broken somewhat.

There are many other issues, too numerous to mention individually, but, the code below is refactored and [most of] the bugs are annotated. It’s still a bit fragile in places (e.g. server can get "Broken pipe") but the basic communication now works.

I’ve used cpp conditionals to denote old vs. new code:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

server code:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#if 1
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#if 0
#define SOCKET_PATH "socket"
#else
#define SOCKET_PATH "funny_socket"
#endif
#define STRMAX      100

#define SUCCESS "Sequence ok"
#define FAILURE "Check failed"

int succcounter = 0;
int totalcounter = 0;

#define dbgprt(_fmt...) 
    printf(_fmt)

#if 1
typedef struct tsk {
    int tsk_sock;
    pthread_t tsk_tid;
    void *tsk_rtn;
    struct tsk *tsk_next;
} tsk_t;
tsk_t *tsklist = NULL;
#endif

void *
average_function(void *arg)
{
#if 0
    int so = (int) arg;
#else
    tsk_t *tsk = arg;
    int so = tsk->tsk_sock;
#endif
    int done, n;
    int sum = 0;
    int *num, m;
    float average;
// NOTE/FRAGILE: this must match str in client
#if 0
    char str[100];
#else
    char str[STRMAX];
#endif
    long code = 0;

#if 0
    done = 1;
#else
    done = 0;
#endif

    do {
        // get count
#if 0
        n = recv(so, str, 4, 0);
#else
        n = recv(so, &m, sizeof(m), 0);
        if (n < 0) {
            perror("recv/m");
            code = 1;
            break;
        }

        // client wants a stop
        if (m < 0)
            break;
#endif

        // allocate space for array
#if 0
        m = (int) str;
        num = (int *) malloc(m);
#else
        num = malloc(sizeof (*num) * m);
#endif

        for (int i = 0; i < m; i++) {
#if 0
            n = recv(so, str, sizeof(num[m]), 0);
            num[i] = (int) str[i];
#else
            n = recv(so, &num[i], sizeof(*num), 0);
#endif
        }

        // compute average
// NOTE/BUG: must reset sum to 0 for subsequent iterations
#if 1
        sum = 0;
#endif
        for (int i = 0; i < m; i++) {
            sum += num[i];
        }
        average = sum / m;

// NOTE/BUG: these are sent out-of-order relative to client
// NOTE/BUG: client reads 100 for message but we do a short send based on
// strlen
#if 0
        if (average > 10) {
            send(so, &average, sizeof(average), 0);
            send(so, SUCCESS, strlen(SUCCESS), 0);
            succcounter++;
            totalcounter++;
        }
        else {
            average = 0;
            send(so, &average, sizeof(average), 0);
            send(so, FAILURE, strlen(FAILURE), 0);
            totalcounter++;
        }
#else

        if (average > 10) {
            strcpy(str,SUCCESS);
            succcounter++;
        }
        else {
            strcpy(str,FAILURE);
            average = 0;
        }

        send(so, str, sizeof(str), 0);
        send(so, &average, sizeof(average), 0);

        totalcounter++;
#endif

#if 0
        if (n == 1)
            done = 1;
#endif

#if 1
        // free space for array
        free(num);
#endif
    } while (!done);

    close(so);

    dbgprt("average_function: exit with %dn",code);
#if 0
    pthread_exit(NULL);
#else
    return (void *) code;
#endif
}

int
main(void)
{
    int s, s2, i, t, len;
    struct sockaddr_un local, remote;
    pthread_t thread[50];
#if 1
    int err;
#endif

    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    local.sun_family = AF_UNIX;
    strcpy(local.sun_path, SOCKET_PATH);

    unlink(local.sun_path);

// NOTE/BUG: must enable for reuse to prevent bind failure
#if 0
    int enable = 1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
#endif

    len = strlen(local.sun_path) + sizeof(local.sun_family);
    if (bind(s, (struct sockaddr *) &local, len) == -1) {
        perror("bind");
        exit(1);
    }

    if (listen(s, 5) == -1) {
        perror("listen");
        exit(1);
    }

    i = 0;

    for (;;) {
        t = sizeof(remote);

        if ((s2 = accept(s, (struct sockaddr *) &remote, &t)) == -1) {
            perror("accept");
            exit(1);
        }

// NOTE/BUG: this will eventually overrun thread -- we need to do pthread_join
// and clean up
#if 0
        pthread_create(&(thread[i++]), NULL, average_function, (void *) s2);
#else
        tsk_t *tsk;
        tsk_t *prev;
        tsk_t *next;

        tsk = malloc(sizeof(*tsk));
        tsk->tsk_sock = s2;
        pthread_create(&tsk->tsk_tid, NULL, average_function, tsk);
        dbgprt("created: %dn",tsk->tsk_tid);

        tsk->tsk_next = tsklist;
        tsklist = tsk;

// FIXME/CAE -- this is broken
#if 1
        continue;
#endif
        prev = NULL;
        for (tsk = tsklist;  tsk != NULL;  tsk = next) {
            next = tsk->tsk_next;

            // NOTE: this is non-standard but other ways to reclaim are more
            // complicated
            err = pthread_tryjoin_np(tsk->tsk_tid,&tsk->tsk_rtn);

            if (err <= 0) {
                prev = tsk;
                continue;
            }

            dbgprt("joined: %dn",tsk->tsk_tid);

            if (prev != NULL)
                prev->tsk_next = next;
            else
                tsklist = next;
            free(tsk);
        }
#endif
    }

    return 0;
}

client code:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#if 1
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>

// NOTE: change name to make a grep of netstat easy to find
#if 0
#define SOCKET_PATH "socket"
#else
#define SOCKET_PATH "funny_socket"
#endif
#if 0
#define SOCKET_PATH "socket"
#else
#define SOCKET_PATH "funny_socket"
#endif
#define STRMAX      100

int
getprompt(const char *prompt,char *str,int len)
{
    char buf[1000];

    if (str == NULL) {
        str = buf;
        len = sizeof(buf);
    }

    printf("%s ",prompt);
    fflush(stdout);

    if (fgets(str,len,stdin) == NULL)
        exit(1);

    str[strcspn(str,"n")] = 0;

    int val = atoi(str);

    return val;
}

int
main(void)
{
    int s, len, t, N = 0;
    int *arr;
// NOTE/FRAGILE: this must match str in server
#if 0
    char str[100];
#else
    char str[STRMAX];
#endif
    char ch;
    float avg = 0;
    struct sockaddr_un remote;

    // check if socket was created
    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    // passing the values to the struct
    remote.sun_family = AF_UNIX;
    strcpy(remote.sun_path, SOCKET_PATH);

    // connect and check
    len = strlen(remote.sun_path) + sizeof(remote.sun_family);
    if (connect(s, (struct sockaddr *) &remote, len) == -1) {
        perror("connect");
        exit(1);
    }

    setbuf(stdout,NULL);

#if 0
    do {
#else
    while (1) {
#endif
#if 0
        printf("Give array dimension: ");
        scanf("%d", &N);
#else
        N = getprompt("Give array dimension:",NULL,0);
#endif

// NOTE/BUG: does not allocate sufficient space
        // dynamicly allocating memory for the array
#if 0
        arr = (int *) malloc(N);
#else
        arr = malloc(sizeof(*arr) * N);
#endif

        // populating the array
        for (int i = 0; i < N; i++) {
#if 0
            printf("Give array element: ");
            scanf("%d", &arr[i]);
#else
            arr[i] = getprompt("Give array element:",NULL,0);
#endif
        }

        // send(file descriptor,void *buff,size,flags) sending N
// NOTE/BETTER: no need to cast as send uses "void *"
#if 0
        send(s, (char *) &N, sizeof(int), 0);
#else
        send(s, &N, sizeof(N), 0);
#endif

        // sending array elements
        for (int i = 0; i < N; i++) {
#if 0
            send(s, (char *) &arr[i], sizeof(arr), 0);
#else
            send(s, &arr[i], sizeof(*arr), 0);
#endif
        }

// NOTE/BETTER -- this is fragile if the length of str is ever changed above
#if 0
        t = recv(s, str, 100, 0);
#else
        t = recv(s, str, sizeof(str), 0);
        if (t < 0) {
            perror("recv/str");
            exit(1);
        }
#endif
        str[t] = '';

        // receive confirmation or failure
        printf("Server sent this message %sn", str);

// NOTE/BETTER: use sizeof
        // receiving average and printing it
#if 0
        recv(s, (char *) &avg, sizeof(float), 0);
#else
        t = recv(s, &avg, sizeof(avg), 0);
        if (t < 0) {
            perror("recv/avg");
            exit(1);
        }
#endif
        if (avg != 0) {
            printf("Server also sent the average which is %.2fn", avg);
        }

        free(arr);

#if 0
        printf("Do you want to continue?(Y/N)");
        ch = getchar();
    } while ((ch != 'N') && (ch == 'n'));
#else
        getprompt("Do you want to continue (Y/N)?",str,sizeof(str));
        ch = str[0];
        if (ch == 'n')
            break;
        if (ch == 'N')
            break;
    }
#endif

    // send stop to server
#if 1
    N = -1;
    send(s, &N, sizeof(N), 0);
#endif

    close(s);

    return 0;
}

UPDATE:

I’ve come up with version of the server code that correctly cleans up after a thread has terminated along with a fix for the SIGFPE:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#if 1
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <poll.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define SOCKET_PATH "funny_socket"
#define STRMAX      100
#define MAXCONN     5

#define SUCCESS "Sequence ok"
#define FAILURE "Check failed"

int succcounter = 0;
int totalcounter = 0;

#define dbgprt(_fmt...) 
    printf(_fmt)

//#define USETID
typedef struct tsk {
    int tsk_xid;
    pid_t tsk_tid;
    pthread_t tsk_pthr;
    int tsk_busy;
    int tsk_sock;
    void *tsk_rtn;
    int tsk_done;
} tsk_t;
#define TSKFORALL(_tsk) 
    _tsk = tsklist;  _tsk < &tsklist[MAXCONN];  ++tsk
tsk_t tsklist[MAXCONN];

void *
average_function(void *arg)
{
    tsk_t *tsk = arg;
    int so = tsk->tsk_sock;
    pid_t tid = syscall(SYS_gettid);
    int n;
    int sum = 0;
    int m;
    int *num = NULL;
    float average;
    char str[STRMAX];
    long code = 0;

    atomic_store(&tsk->tsk_tid,tid);

    while (1) {
        // get count
        n = recv(so, &m, sizeof(m), 0);
        if (n <= 0) {
            if (n < 0)
                perror("recv/m");
            else
                dbgprt("short read mn");
            code = 1;
            break;
        }

        // client wants a stop
        if (m <= 0)
            break;

        // allocate space for array
        num = malloc(sizeof (*num) * m);

        for (int i = 0; i < m; i++) {
            n = recv(so, &num[i], sizeof(*num), 0);
        }

        // compute average
        sum = 0;
        for (int i = 0; i < m; i++) {
            sum += num[i];
        }
        average = sum / m;

        if (average > 10) {
            strcpy(str,SUCCESS);
            succcounter++;
        }
        else {
            strcpy(str,FAILURE);
            average = 0;
        }

        send(so, str, sizeof(str), 0);
        send(so, &average, sizeof(average), 0);

        totalcounter++;

    }

    close(so);

    // free space for array
    if (num != NULL)
        free(num);

    dbgprt("average_function: exit with %ldn",code);

    return (void *) code;
}

void
tskreapall(void)
{
    int err;
    tsk_t *tsk;
    pid_t tid = 0;

    int xid = 0;
    for (TSKFORALL(tsk)) {
        tsk->tsk_xid = ++xid;

        if (! tsk->tsk_busy)
            continue;

#ifdef USETID
        pid_t pid = getpid();
        tid = atomic_load(&tsk->tsk_tid);
        err = syscall(SYS_tgkill,pid,tid,0);
        if (err >= 0)
            continue;
#else
        err = pthread_kill(tsk->tsk_pthr,0);
        if (err)
            continue;
#endif

        dbgprt("tskreapall: joining %d/%dn",tsk->tsk_xid,tid);

        err = pthread_join(tsk->tsk_pthr,&tsk->tsk_rtn);
        tsk->tsk_busy = 0;
    }
}

tsk_t *
tskfind(void)
{
    tsk_t *tsk;
    tsk_t *rtn = NULL;

    for (TSKFORALL(tsk)) {
        if (! tsk->tsk_busy) {
            rtn = tsk;
            break;
        }
    }

    if (rtn != NULL) {
        atomic_store(&rtn->tsk_tid,0);
        rtn->tsk_busy = 1;
    }

    return rtn;
}

int
main(void)
{
    int s, s2, len;
    struct sockaddr_un local, remote;
    socklen_t t;
    int err;

    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    local.sun_family = AF_UNIX;
    strcpy(local.sun_path, SOCKET_PATH);

    unlink(local.sun_path);

    len = strlen(local.sun_path) + sizeof(local.sun_family);
    if (bind(s, (struct sockaddr *) &local, len) == -1) {
        perror("bind");
        exit(1);
    }

    if (listen(s, MAXCONN) == -1) {
        perror("listen");
        exit(1);
    }

    for (;;) {
        struct pollfd polls[1];
        struct pollfd *fd = &polls[0];
        fd->fd = s;
        fd->events = ~0;

        err = poll(fd,1,100);
        if (err < 0)
            err = -errno;

        tskreapall();

        if (err != 1)
            continue;

        dbgprt("err=%d revents=%4.4Xn",err,fd->revents);

        t = sizeof(remote);
        s2 = accept(s, (struct sockaddr *) &remote, &t);
        if (s2 == -1) {
            perror("accept");
            exit(1);
        }

        tsk_t *tsk;
        pid_t tid = 0;

        tsk = tskfind();
        tsk->tsk_sock = s2;
        pthread_create(&tsk->tsk_pthr, NULL, average_function, tsk);

        // wait for thread to start
        while (1) {
            tid = atomic_load(&tsk->tsk_tid);
            if (tid)
                break;
        }

        dbgprt("created: %d/%dn",tsk->tsk_xid,tid);
    }

    return 0;
}
Answered By: Anonymous

Related Articles

  • How do SO_REUSEADDR and SO_REUSEPORT differ?
  • How can I resolve Web Component Testing error?
  • Callback functions in C++
  • get an unknown amount of numbers from the user and get the…
  • Fastest way to iterate over all the chars in a String
  • Why does C++ code for testing the Collatz conjecture run…
  • Adding gif image in an ImageView in android
  • Why is 2 * (i * i) faster than 2 * i * i in Java?
  • Possible to use a let within an if/cond?
  • exec failed because the name not a valid identifier?

Disclaimer: This content is shared under creative common license cc-by-sa 3.0. It is generated from StackExchange Website Network.

Post navigation

Previous Post:

Multiple json from a database using php

Next Post:

Error looping through an array with NgFor

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

  • Get code errors & solutions at akashmittal.com
© 2022 Fix Code Error