Tam211
Tam211

Reputation: 733

C socket programming - write() from server writes to server instead of client

I'm working on a TCP client server program which is supposed to support several clients using threads.

The socket creation, connection, bind and accept work as expected since I receive no errors when running the code. However whenever I try to read() from the server the code enters an infinite loop and nothing happens.

I tried writing from the server first and the write result was written to the server's terminal.

Client code:

#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>

#define FILE_ADDR   "/dev/urandom"

int main(int argc, char *argv[]) {

    //Get command line arguments
    unsigned int port = atoi(argv[2]);
    int length = atoi(argv[3]); //Number of bytes to read
    char* buffer = malloc(length * sizeof(char)); //Buffer to hold data read from file
    char* recvBuf = malloc(10 * sizeof(char)); // Buffer to hold response from server

    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_in serv_addr;
    int sockfd = -1;
    //int rv;
    //char ip[100];

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
    if (rv != 0) {
        perror("getaddrinfo error\n");
        exit(1);
    }
    for (p = servinfo; p != NULL; p = p->ai_next) {
        //Initialize socket
        sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (sockfd < 0)
            continue;
        //Initialize connection
        rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
        if (rv == 0)
            break;
        close(sockfd);
        sockfd = -1;
    }
    //   inet_aton(ip, &h.sin_addr);
    freeaddrinfo(servinfo);

    //Open file for reading
    FILE *fp;
    fp = fopen(FILE_ADDR, "r");
    if (fp == NULL) {
        perror("Error in file open\n");
    }
    printf("file opened\n");
    size_t numOfbytesRead = fread(buffer, sizeof(char), length, fp);
    if (numOfbytesRead != length) {
        perror("Error reading from file\n");
    }
    printf("Buffer is %s\n", buffer);

    char* ptr;
    unsigned int N = strtoul(argv[3],&ptr,10);
    int convertedNum = htonl(N);

    if (write(sockfd, &convertedNum, sizeof(unsigned int)) < 0) {   //Send number of bytes
        perror("error writing to socket");
    }
    if (write(sockfd, buffer, sizeof(buffer) < 0)) {//Send bytes read from file
        perror("error writing to socket");

    }

    printf("send is done \n");

    int bytes_read = read(sockfd, recvBuf, sizeof(recvBuf)); //Recieve response from server
    if (bytes_read <= 0) {
        perror("Error in recieving result from server\n");
    }

    unsigned int C = 0;
    sprintf(recvBuf[0], "%d", C);

    fclose(fp);
    printf("# of printable characters: %u\n", C);
    exit(0);
    free(buffer);
    free(recvBuf);

}

Server code:

#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>

static volatile int keepRunning = 1;
int pcc_total[159];

void intHandler(int dummy) {
    keepRunning = 0;
}

void *compute(void *socket_desc) {
    int count = 0;
    int sock = *(int*) socket_desc;
    printf("now will allocate N \n");
    int n=0;
    if (write(sock, "hi", 2) < 0) { //Send number of bytes
        perror("error writing to socket\n");
    }
    if (read(sock, &n, sizeof(unsigned int)) < 0) {
        perror("Error reading from socket\n");
        exit(1);
    }
    int N = ntohl(n);
    printf("len is %d\n", N);
    char* data = calloc(N, sizeof(char));
    int len = read(sock, data, N);
    printf("data is %s\n", data);
    if (len < 0) {
        perror("Error reading from socket\n");
        exit(1);
    }
    for (int i = 0; i < len; i++) {
        int tmp = 0;
        sprintf(data[i], "%d", tmp);
        if (tmp >= 32 & tmp <= 126) {
            count++;

            __sync_fetch_and_add(&pcc_total[tmp], 1);
        }
    }
    char scount[100];
    atoi(count);
    write(sock, count, strlen(scount));
    free(data);
    pthread_exit(NULL);
    close(sock);
    exit(0);
}

int main(int argc, char *argv[]) {

    unsigned int port = atoi(argv[1]);
    signal(SIGINT, intHandler);

    int socket_desc, client_sock, c, *new_sock;
    struct sockaddr_in server, client;
    c = sizeof(struct sockaddr_in);

    socket_desc = socket( AF_INET, SOCK_STREAM, 0);
    if (socket_desc == -1) {
        perror("Could not create socket");
        exit(1);
    }
    printf("socket created\n");
    memset(&server, 0, c);

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(port);

    if (0 != bind(socket_desc, (struct sockaddr*) &server, sizeof(server))) {
        perror("\n Error : Bind Failed \n");
        exit(1);
    }
    printf("bind created\n");
    if (0 != listen(socket_desc, 10)) {
        perror("\n Error : Listen Failed \n");
        exit(1);
    }
    printf("listen created\n");
    while (keepRunning) {
        client_sock = accept(socket_desc, (struct sockaddr *) &client,
                (socklen_t*) &c);
        if (client_sock < 0) {
            perror("\n Error : Accept Failed\n");
            exit(1);
        }
        printf("accept created\n");
        pthread_t tid;
        new_sock = malloc(100*sizeof(int));
        *new_sock = client_sock;
        if ((pthread_create(&tid, NULL, compute, (void*) new_sock)) < 0) {
            perror("could not create thread\n");
            exit(1);
        }
        printf("thread created\n");
        // close socket
        close(client_sock);
        free(new_sock);
        pthread_join(tid, NULL);
    }

    exit(0);

}

I run the code with the following commands:

gcc -std=c99 -O3 -Wall -o pcc_server pcc_server.c -pthread

gcc -std=gnu99 -O3 -Wall -o pcc_client pcc_client.c

Upvotes: 0

Views: 19400

Answers (3)

Remy Lebeau
Remy Lebeau

Reputation: 595402

There are a number of problems with your code.

On the client side:

  • When calling fread(), you need to use "rb" instead of "r".

  • when calling printf() to output the file data that was actually read, you are not null-terminating buffer, or passing its length to printf(). You need to do so.

  • You are assigning the return value of htonl() to an int instead of an unsigned int.

  • when calling write() to send the buffer, you are using sizeof(buffer) when you should be using length or N instead (and why are you using two separate variables to hold the same command-line parameter value?). buffer is a pointer to memory allocated with malloc(), so sizeof(buffer) is the same as sizeof(void*), which is not what you want. Also, you are not even calling write() correctly, because your parenthesis are all wrong (they are correct on the previous write() call when sending convertedNum).

  • likewise, when calling read() to read the recvBuf, you are using sizeof(recvBuf) when you should be using 10 instead, sicne recvBuf is also a pointer to malloced memory.

  • you are not reading the "hi" greeting that the server sends to the client upon connection, so you lump in those bytes with the bytes of the following size value of the next message, and thus end up with a corrupted C value.

On the server side:

  • your compute thread routine sends a "hi" greeting to the client, but it does not use any kind of delimiter, like prefixing the greeting with its length, or terminating it with a line break or null character or other unique character, to separate it from any subsequent data. You should always delimit your messages in some manner.

  • you are closing the accepted socket and freeing the malloced new_sock as soon as you create a worker thread to handle that client. You are ripping away memory from behind the thread's proverbial back. The thread needs to be the one to close the socket and free the memory when it is done using them, not the accept loop.

    The thread does attempt to close the socket (but not free the memory), but after it calls pthread_exit() first, which is wrong. pthread_exit() terminates the calling thread, so it needs to be the last thing that the thread calls (DO NOT call exit()!). In fact, don't even call pthread_exit() directly at all, just return from compute(), the pthreads library will then call pthread_exit() for you, passing it whatever void* value you choose to return.

  • your accept loop should not be calling pthread_join() at all. It blocks the calling thread until the specified thread terminates. That defeats the whole purpose of using threads to handle your clients, and prevents your server from accepting more than 1 client at a time. If you are going to use pthread_join() at all, it should be after the accept loop has ended, so you can wait for any worker threads that may still be running before exiting the app. But that also means keeping track of the pthread_t values that pthread_create() returns, which is more work.

With that said, try this code instead:

Client code:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>

#define FILE_ADDR   "/dev/urandom"

char* readMsg(int sockfd, size_t *msgSize)
{
    *msgSize = 0;

    unsigned int length = 0;
    int bytes_read = read(sockfd, &length, sizeof(length)); //Receive number of bytes
    if (bytes_read <= 0) {
        perror("Error in receiving message from server\n");
        return NULL;
    }
    length = ntohl(length);

    char *buffer = malloc(length+1);
    if (!buffer) {
        perror("Error in allocating memory to receive message from server\n");
        return NULL;
    }

    char *pbuf = buffer;
    unsigned int buflen = length;
    while (buflen > 0) {
        bytes_read = read(sockfd, pbuf, buflen); // Receive bytes
        if (bytes_read <= 0) {
            perror("Error in receiving message from server\n");
            free(buffer);
            return NULL;
        }
        pbuf += bytes_read;
        buflen -= bytes_read;
    }

    *msgSize = length;
    return buffer;
}

int sendMsg(int sockfd, char *msg, size_t msgSize)
{
    unsigned int convertedNum = htonl(msgSize);
    if (write(sockfd, &convertedNum, sizeof(convertedNum)) < 0) { //Send number of bytes
        perror("error writing to socket");
        return -1;
    }

    if (write(sockfd, msg, msgSize) < 0) { //Send bytes
        perror("error writing to socket");
        return -1;
    }

    return 0;
}

int main(int argc, char *argv[]) {

    char* ptr;

    //Get command line arguments
    unsigned int port = atoi(argv[2]);
    unsigned int length = strtoul(argv[3], &ptr, 10); //Number of bytes to read
    char* buffer = malloc(length); //Buffer to hold data read from file

    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_in serv_addr;
    int sockfd = -1;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
    if (rv != 0) {
        perror("getaddrinfo error\n");
        return 1;
    }
    for (p = servinfo; p != NULL; p = p->ai_next) {
        //Initialize socket
        sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (sockfd < 0)
            continue;
        //Initialize connection
        rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
        if (rv == 0)
            break;
        close(sockfd);
        sockfd = -1;
    }
    freeaddrinfo(servinfo);

    if (sockfd == -1) {
        perror("socket create/connect error\n");
        return 1;
    }

    size_t msgSize;
    char *msg = readMsg(sockfd, &msgSize);
    if (!msg) {
        close(sockfd);
        return 1;
    }
    printf("%.*s\n", (int)msgSize, msg);
    free(msg);

    //Open file for reading
    FILE *fp = fopen(FILE_ADDR, "rb");
    if (fp == NULL) {
        perror("Error in file open\n");
        close(sockfd);
        return 1;
    }
    printf("file opened\n");

    if (fread(buffer, 1, length, fp) != length) {
        perror("Error reading from file\n");
        fclose(fp);
        close(sockfd);
        return 1;
    }
    fclose(fp);
    printf("Buffer is %.*s\n", (int)length, buffer);

    if (sendMsg(sockfd, buffer, length) != 0) {
        free(buffer);
        close(sockfd);
        return 1;
    }
    free(buffer);
    printf("send is done \n");

    msg = readMsg(sockfd, &msgSize); // response from server
    if (!msg) {
        close(sockfd);
        return 1;
    }
    printf("# of printable characters: %.*s\n", (int)msgSize, msg);
    free(msg);

    return 0;
}

Server code:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>

static volatile int keepRunning = 1;
int pcc_total[159];

void intHandler(int dummy) {
    keepRunning = 0;
}

char* readMsg(int sockfd, size_t *msgSize)
{
    *msgSize = 0;

    unsigned int length = 0;
    int bytes_read = read(sockfd, &length, sizeof(length)); //Receive number of bytes
    if (bytes_read <= 0) {
        perror("Error in receiving message from server\n");
        return NULL;
    }
    length = ntohl(length);

    char *buffer = malloc(length+1);
    if (!buffer) {
        perror("Error in allocating memory to receive message from server\n");
        return NULL;
    }

    char *pbuf = buffer;
    unsigned int buflen = length;
    while (buflen > 0) {
        bytes_read = read(sockfd, pbuf, buflen); // Receive bytes
        if (bytes_read <= 0) {
            perror("Error in receiving message from server\n");
            free(buffer);
            return NULL;
        }
        pbuf += bytes_read;
        buflen -= bytes_read;
    }

    *msgSize = length;
    return buffer;
}

int sendMsg(int sockfd, char *msg, size_t msgSize)
{
    unsigned int convertedNum = htonl(msgSize);
    if (write(sockfd, &convertedNum, sizeof(convertedNum)) < 0) { //Send number of bytes
        perror("error writing to socket");
        return -1;
    }

    if (write(sockfd, msg, msgSize) < 0) { //Send bytes
        perror("error writing to socket");
        return -1;
    }

    return 0;
}

void *compute(void *socket_desc) {
    int sock = * (int*) socket_desc;
    free(socket_desc);

    if (sendMsg(sock, "hi", 2) != 0) {
        perror("error writing to socket\n");
        close(sock);
        return NULL;
    }

    size_t length = 0;
    char *data = readMsg(sock, &length);
    if (!msg) {
        close(sock);
        return NULL;
    }
    printf("len is %d\n", (int)length);
    printf("data is %.*s\n", (int)length, data);

    int count = 0;
    for (size_t i = 0; i < length; i++) {
        // ...
    }
    free(data);

    char scount[20];
    sprintf(scount, "%d", count);
    sendMsg(sock, scount, strlen(scount));

    close(sock);
    return NULL;
}

int main(int argc, char *argv[]) {

    unsigned int port = atoi(argv[1]);
    signal(SIGINT, intHandler);

    int socket_desc, client_sock, c, *new_sock;
    struct sockaddr_in server, client;

    socket_desc = socket( AF_INET, SOCK_STREAM, 0);
    if (socket_desc == -1) {
        perror("Could not create server socket");
        return 1;
    }
    printf("server socket created\n");

    memset(&server, 0, c);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(port);

    if (bind(socket_desc, (struct sockaddr*) &server, sizeof(server)) < 0) {
        perror("\n Error : Bind Failed \n");
        close(socket_desc);
        return 1;
    }
    printf("bind created\n");

    if (listen(socket_desc, 10) < 0) {
        perror("\n Error : Listen Failed \n");
        close(socket_desc);
        return 1;
    }
    printf("listening\n");

    while (keepRunning) {
        c = sizeof(client);
        client_sock = accept(socket_desc, (struct sockaddr *) &client,
                            (socklen_t*) &c);
        if (client_sock < 0) {
            perror("\n Error : Accept Failed\n");
            continue;
        }
        printf("client accepted\n");

        new_sock = malloc(sizeof(int));
        if (!new_sock) {
            perror("\n Error : Malloc Failed\n");
            close(client_sock);
            continue;
        }
        *new_sock = client_sock;

        pthread_t tid;
        if (pthread_create(&tid, NULL, &compute, new_sock) != 0) {
            perror("\n Error : Thread Create Failed\n");
            free(new_sock);
            close(client_sock);
            continue;
        }
        printf("thread created\n");
    }

    close(socket_desc);
    return 0;
}

Upvotes: 6

John Bollinger
John Bollinger

Reputation: 180073

Your server closes the connected socket and frees the memory in which you stored its file handle immediately after launching the thread to handle that connection. You're unlucky that the server only hangs as a result, but you have data races, so formally your program's behavior is undefined.

Since the server isn't going to do anything else until the thread has finished, you might as well move the close() and free() after the pthread_join(). Or, considering that you do join before creating any other threads, how about just calling compute() synchronously instead of creating a new thread for it to run in?

Upvotes: 0

Snohdo
Snohdo

Reputation: 156

I think you should remove the two lines

close(client_sock);
free(new_sock);

in the server code because the newly created thread can't perform on those variables and memory area if it is freed up at such an early point. Can you try the code again without it?

Upvotes: 1

Related Questions