Named pipes for client-server simulation in 2 terminals

759 Views Asked by At

I know the title doesn't explain the problem precisely and I apologize.

I've been writing a program in C of named pipes and the goal of it is to open 2 windows of the terminal, each running either the "Server" or the "Client" file. After the terminals connected to each other via one named pipe, the Client can then send a string to the Server, the server would create a thread that prints the string it had received, reverses it, and then send it back to the Client via another named pipe. Finally, the Client should print the message it had received back from the Server. The client should be able to keep sending strings until the file is exited or the string "exit" is sent.

What my issue is and what I think causes it: Everything works fine when the user enters a single word in the string, but when it sends a sentence with spaces in it, the Client's fscanf, that is meant to read from "toClient" named pipe, slices the sentence and it can only receive one word at a time. So then the next time the client sends a message, it will read the second word of the previous message because it was sliced out and stayed in the named pipe. I tried to use a while loop to keep reading from "toClient" until all the individual words have been taken out and it works like I expected it to, but after the first message the connection hangs and the client can't send new messages. I think it hangs because the while loop doesn't reach EOF for some reason, maybe because both the Client and Server still have the named pipe open.

Server:

/*function that reverses a string*/
    char* revstr(char *str1)  
{  
    int i, len, temp;  
    len = strlen(str1);
      
    for (i = 0; i < len/2; i++)  
    {  
        temp = str1[i];  
        str1[i] = str1[len - i - 1];  
        str1[len - i - 1] = temp;  
    }
    return str1;
}  
    /*function that is run by a thread to handle a client's message*/
    void *threadFunc(void *arg){
        char *string = (char*)arg;
        printf("Received from client: %s\n", string);//print message received to terminal
        /*make a connection to "toClient" named pipe that in order to send back the reversed string*/
        FILE *fdw;
        if (!(fdw = fopen("toClient", "w"))) {
            perror("cannot open fifo file for w") ;
            exit(EXIT_FAILURE) ;
        }
        /*irrelevant*/
        if(strcmp(string,"exit")==0)
        {
            fprintf(fdw, " Done\n") ;
            fflush(fdw) ;
            printf("Shutting down...\n");
            exit(0) ;
        }
        char *string2 = revstr(string);//string2 is the reversed string
        fprintf(fdw, " %s\n", string2) ;//send string2 into the named pipe labeled "toClient"
        fflush(fdw) ;
        printf("Sent message back to client...\n");
    }

int main()
{
    char s[STR_LEN];
    
    FILE *fdr;
    
    if (mkfifo("toServer", 0777) == -1 && errno != EEXIST) {
        perror("cannot create fifo1 file") ;
        exit(EXIT_FAILURE) ;
    }
    if (mkfifo("toClient", 0777) == -1 && errno != EEXIST) {
        perror("cannot create fifo2 file") ;
        exit(EXIT_FAILURE) ;
    }
    
    printf("Waiting for client...\n");
    if (!(fdr = fopen("toServer", "r"))) {
        perror("cannot open fifo file for r") ;
        exit(EXIT_FAILURE) ;
    }
    
    printf("Client found, waiting for message...\n");
    /*this block waits for a message from the client, then creates a thread to handle it*/
    while ( fscanf(fdr, " %s", s) != EOF){
        int retcode;
        pthread_t t1;
        retcode = pthread_create(&t1,NULL,&threadFunc,(void *)(s));
        if(retcode!=0)
            printf("Create thread failed with error %d\n", retcode);
        pthread_join(t1,NULL);
    }
    printf("Client disconnected\n") ;
    return EXIT_SUCCESS ;
} 

Client:

{
    char s[STR_LEN];
    FILE *fdw;
    FILE *fdr;
    
    if (mkfifo("toServer", 0777) == -1 && errno != EEXIST) {
        perror("cannot create fifo file") ;
        exit(EXIT_FAILURE) ;
    }
    if (mknod("toClient", 0777,0) == -1 && errno != EEXIST) {
        perror("cannot create fifo file") ;
        exit(EXIT_FAILURE) ;
    }
    if (!(fdw = fopen("toServer", "w"))) {
        perror("cannot open fifo file for w") ;
        exit(EXIT_FAILURE) ;
    }
    puts("Connected to server, enter a message:\n");
    /*the user now enters a message into the terminal*/
    while ( fgets(s, STR_LEN, stdin) != NULL) {
        printf("Sent: %s",s);//print the message to terminal
        fprintf(fdw, " %s\n", s) ;//send the message into the named pipe labeled "toServer"
        fflush(fdw) ;
        /*connect to the server to receive it's response using the named pipe labeled "toClient"*/
        if (!(fdr = fopen("toClient", "r"))) {
            perror("cannot open fifo file for r") ;
            exit(EXIT_FAILURE) ;
        }
        /*this is where my problem is - this block is essentially meant to read from the named pipe "toClient"*/
        if/*while*/ ( fscanf(fdr, " %s", s) != EOF )
        {
            printf("Received from server: %s\n", s);//print the response received to the terminal
            /*Irrelevant*/
            if(strcmp(s,"Done")==0)
                exit(0);
        }
    }
    return EXIT_SUCCESS ;
    } 

Terminals when using if on fscanf: enter image description here

Terminals when using while on fscanf: enter image description here

I know this thread is really long and the problem is probably due to bad code design but I have no idea what to do after almost 8 hours of trying and being relatively inexperienced with the subject .(It's part of an OS course I'm taking in uni)

1

There are 1 best solutions below

0
On

Well, this is how scanf with %s specifier works. If you want to read whole line using scanf then use %[^\n]s, which means - read until newline character. Anyways it would be better to use fgets.