Search code examples
cforkipc

error in msgsnd - msgrcv, I can't view the second string sent


Hi (sorry for my bad English), I'm trying to practice Linux to manipulate message queues in IPC. I have created a program that allows one process (child 1) to send the message in the queue, while the other process (child 2) reads that message. this message is defined as a structure composed of two strings (str1 and str2) and a type (long). The goal that I want to realize and read the two strings but the result is that the program in the reads only one. Thanks.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/wait.h>
#include <time.h>

typedef struct{
    long int type;
    char str1[255];
    char str2[255];
}msg;

void sender(int fd_msg){
    msg message;
    char *string1 = "Hello";
    char *string2 = "World"; 

    strcpy(message.str1,string1);
    strcpy(message.str2,string2);

    message.type = 1;
    if(msgsnd(fd_msg,&message,strlen(message.str1)+1,0) == -1){
        printf("Error sending message at message 1 queue %d --> %s\n",errno,strerror(errno));
        return;
    }

    message.type = 2;
    if(msgsnd(fd_msg,&message,strlen(message.str2)+1,0) == -1){
        printf("Error sending message at message 2 queue %d --> %s\n",errno,strerror(errno));
        return;
    }

    if(printf("Message sent at the queue: %s -- %s\n",message.str1,message.str2) < 0){
        printf("Error printing sent messages %d --> %s\n",errno,strerror(errno));
        msgctl(fd_msg,IPC_RMID,NULL);           
        return; 
    }
    return;
}

void receiver(int fd_msg){
    msg message;
    char string1[255];
    char string2[255];
    
    sleep(1);
    if(msgrcv(fd_msg,&message,sizeof(message)-sizeof(long int),1,0) == -1){
        printf("error reiceiving message type 1 %d --> %s\n",errno,strerror(errno));
        return;

    }
    if(msgrcv(fd_msg,&message,sizeof(message)-sizeof(long int),2 ,0) == -1){
        printf("error reiceiving message type2 %d --> %s\n",errno,strerror(errno));
        return;
    }   

    if(printf("Message reiceved! : %s -- %s\n",message.str1,message.str2) < 0){
        printf("Error printing received message %d --> %s\n",errno,strerror(errno));
        msgctl(fd_msg,IPC_RMID,NULL);
        return;
    }

    strcpy(string1,message.str1);
    strcpy(string2,message.str2);
    //printf("string2: %s\n",messaggio.str2);

    return;
}

int main(int argc, char* argv[]){
    int fd_msg = -1;
    int key_msg = -1;
    pid_t p1;
    pid_t p2;

    key_msg = ftok("../Example/in.txt",55);   
    if((fd_msg = msgget(key_msg,IPC_CREAT | 0770)) == -1){             
        printf("Error creation message queue %d --> %s\n",errno, strerror(errno));
        return -1;
    }

    if((p1 = fork()) == 0){
        //child 1 (writer)
        sender(fd_msg);
        exit(0);
    }

    waitpid(p1,NULL,0);

    if((p2 = fork()) == 0){
        //child 2 (reader)
        receiver(fd_msg);
        exit(0);
    }

    waitpid(p2,NULL,0);

    msgctl(fd_msg,IPC_RMID,NULL);      
    return 0;
} 

Solution

  • You should send only one message and a length of sizeof(message)-sizeof(long int) just as you do for receiving the message. That's because str1 and str2 have fixed lengths of 255.

    You want only one message (that sends the data for both strings in a single message).

    As you have it, the "short" payload length that you [should] send is 255 + 5 + 1 and not 5 + 1 + 5 + 1

    There are a number of ways to do this:

    1. Send one message with both strings using the full sizeof(msg)
    2. Send one message with full size of str1 but short length of str2
    3. Combine str1 and str2 into a single buffer and [manually] concatenate/split the strings
    4. Use a single buffer and send two messages, one for each string, using just the short length of each string.

    Example #1:

    Here is the corrected code using the full message size:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/msg.h>
    #include <sys/ipc.h>
    #include <sys/wait.h>
    #include <time.h>
    
    typedef struct {
        long int type;
        char str1[255];
        char str2[255];
    } msg;
    
    void
    sender(int fd_msg)
    {
        msg message;
        char *string1 = "Hello";
        char *string2 = "World";
    
        strcpy(message.str1, string1);
        strcpy(message.str2, string2);
    
        size_t len = sizeof(message) - sizeof(long);
    
        message.type = 1;
        printf("Sending length %zu\n",len);
        if (msgsnd(fd_msg, &message, len, 0) == -1) {
            printf("Error sending message at message 1 queue %d --> %s\n", errno, strerror(errno));
            return;
        }
    
        if (printf("Message sent at the queue: %s -- %s\n", message.str1, message.str2) < 0) {
            printf("Error printing sent messages %d --> %s\n", errno, strerror(errno));
            msgctl(fd_msg, IPC_RMID, NULL);
            return;
        }
        return;
    }
    
    void
    receiver(int fd_msg)
    {
        msg message;
        char string1[255];
        char string2[255];
    
        sleep(1);
    
        ssize_t len = msgrcv(fd_msg, &message, sizeof(message) - sizeof(long int), 1, 0);
        if (len == -1) {
            printf("error receiving message type 1 %d --> %s\n", errno, strerror(errno));
            return;
    
        }
    
        printf("Received length: %zu\n",len);
        if (printf("Message received! : %s -- %s\n", message.str1, message.str2) < 0) {
            printf("Error printing received message %d --> %s\n", errno, strerror(errno));
            msgctl(fd_msg, IPC_RMID, NULL);
            return;
        }
    
        strcpy(string1, message.str1);
        strcpy(string2, message.str2);
        // printf("string2: %s\n",messaggio.str2);
    
        return;
    }
    
    int
    main(int argc, char *argv[])
    {
        int fd_msg = -1;
        int key_msg = -1;
        pid_t p1;
        pid_t p2;
    
        key_msg = ftok("../Example/in.txt", 55);
        if ((fd_msg = msgget(key_msg, IPC_CREAT | 0770)) == -1) {
            printf("Error creation message queue %d --> %s\n", errno, strerror(errno));
            return -1;
        }
    
        if ((p1 = fork()) == 0) {
            // child 1 (writer)
            sender(fd_msg);
            exit(0);
        }
    
        waitpid(p1, NULL, 0);
    
        if ((p2 = fork()) == 0) {
            // child 2 (reader)
            receiver(fd_msg);
            exit(0);
        }
    
        waitpid(p2, NULL, 0);
    
        msgctl(fd_msg, IPC_RMID, NULL);
        return 0;
    }
    

    Here is the program output:

    Sending length 512
    Message sent at the queue: Hello -- World
    Received length: 512
    Message received! : Hello -- World
    

    Example #2:

    Here is the program using a "short" length for str2:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/msg.h>
    #include <sys/ipc.h>
    #include <sys/wait.h>
    #include <time.h>
    
    typedef struct {
        long int type;
        char str1[255];
        char str2[255];
    } msg;
    
    void
    sender(int fd_msg)
    {
        msg message;
        char *string1 = "Hello";
        char *string2 = "World";
    
        strcpy(message.str1, string1);
        strcpy(message.str2, string2);
    
        size_t len = sizeof(message.str1) + strlen(message.str2) + 1;
    
        message.type = 1;
        printf("Sending length %zu\n",len);
        if (msgsnd(fd_msg, &message, len, 0) == -1) {
            printf("Error sending message at message 1 queue %d --> %s\n", errno, strerror(errno));
            return;
        }
    
        if (printf("Message sent at the queue: %s -- %s\n", message.str1, message.str2) < 0) {
            printf("Error printing sent messages %d --> %s\n", errno, strerror(errno));
            msgctl(fd_msg, IPC_RMID, NULL);
            return;
        }
        return;
    }
    
    void
    receiver(int fd_msg)
    {
        msg message;
        char string1[255];
        char string2[255];
    
        sleep(1);
    
        ssize_t len = msgrcv(fd_msg, &message, sizeof(message) - sizeof(long int), 1, 0);
        if (len == -1) {
            printf("error receiving message type 1 %d --> %s\n", errno, strerror(errno));
            return;
    
        }
    
        printf("Received length: %zu\n",len);
        if (printf("Message received! : %s -- %s\n", message.str1, message.str2) < 0) {
            printf("Error printing received message %d --> %s\n", errno, strerror(errno));
            msgctl(fd_msg, IPC_RMID, NULL);
            return;
        }
    
        strcpy(string1, message.str1);
        strcpy(string2, message.str2);
        // printf("string2: %s\n",messaggio.str2);
    
        return;
    }
    
    int
    main(int argc, char *argv[])
    {
        int fd_msg = -1;
        int key_msg = -1;
        pid_t p1;
        pid_t p2;
    
        key_msg = ftok("../Example/in.txt", 55);
        if ((fd_msg = msgget(key_msg, IPC_CREAT | 0770)) == -1) {
            printf("Error creation message queue %d --> %s\n", errno, strerror(errno));
            return -1;
        }
    
        if ((p1 = fork()) == 0) {
            // child 1 (writer)
            sender(fd_msg);
            exit(0);
        }
    
        waitpid(p1, NULL, 0);
    
        if ((p2 = fork()) == 0) {
            // child 2 (reader)
            receiver(fd_msg);
            exit(0);
        }
    
        waitpid(p2, NULL, 0);
    
        msgctl(fd_msg, IPC_RMID, NULL);
        return 0;
    }
    

    Here is the program output:

    Sending length 261
    Message sent at the queue: Hello -- World
    Received length: 261
    Message received! : Hello -- World
    

    Example #3:

    Using separate str1 and str2 forces us to send the full size for str1 even if the actual string length is less.

    To send a single message that has both strings and sends only the length we need would require combining str1 and str2 into a single buffer (e.g. strs). This requires that we concatenate the two strings into the buffer.

    This also requires that the receiver recover the two separate strings.

    Here is the code:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/msg.h>
    #include <sys/ipc.h>
    #include <sys/wait.h>
    #include <time.h>
    
    typedef struct {
        long int type;
        char strs[512];
    } msg;
    
    void
    sender(int fd_msg)
    {
        msg message;
        char *string1 = "Hello";
        char *string2 = "World";
    
        char *cp = message.strs;
    
        strcpy(cp, string1);
        cp += strlen(cp) + 1;
    
        strcpy(cp, string2);
        cp += strlen(cp) + 1;
    
        size_t len = cp - message.strs;
    
        message.type = 1;
        printf("Sending length %zu\n",len);
        if (msgsnd(fd_msg, &message, len, 0) == -1) {
            printf("Error sending message at message 1 queue %d --> %s\n", errno, strerror(errno));
            return;
        }
    }
    
    void
    receiver(int fd_msg)
    {
        msg message;
        char string1[255];
        char string2[255];
    
        sleep(1);
    
        ssize_t len = msgrcv(fd_msg, &message, sizeof(message) - sizeof(long int), 1, 0);
        if (len == -1) {
            printf("error receiving message type 1 %d --> %s\n", errno, strerror(errno));
            return;
    
        }
    
        printf("Received length: %zu\n",len);
    
        char *cp = message.strs;
    
        strcpy(string1, cp);
        cp += strlen(cp) + 1;
    
        strcpy(string2, cp);
        cp += strlen(cp) + 1;
    
        printf("string1: %s\n",string1);
        printf("string2: %s\n",string2);
    
        return;
    }
    
    int
    main(int argc, char *argv[])
    {
        int fd_msg = -1;
        int key_msg = -1;
        pid_t p1;
        pid_t p2;
    
        key_msg = ftok("../Example/in.txt", 55);
        if ((fd_msg = msgget(key_msg, IPC_CREAT | 0770)) == -1) {
            printf("Error creation message queue %d --> %s\n", errno, strerror(errno));
            return -1;
        }
    
        if ((p1 = fork()) == 0) {
            // child 1 (writer)
            sender(fd_msg);
            exit(0);
        }
    
        waitpid(p1, NULL, 0);
    
        if ((p2 = fork()) == 0) {
            // child 2 (reader)
            receiver(fd_msg);
            exit(0);
        }
    
        waitpid(p2, NULL, 0);
    
        msgctl(fd_msg, IPC_RMID, NULL);
        return 0;
    }
    

    Here is the program output:

    Sending length 12
    Received length: 12
    string1: Hello
    string2: World
    

    Example #4:

    I'd probably do example #1 and send the full struct length.

    Or, I'd use the combined buffer from example #3, but send two separate messages, one for each string:

    Here is the code:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/msg.h>
    #include <sys/ipc.h>
    #include <sys/wait.h>
    #include <time.h>
    
    typedef struct {
        long int type;
        char str[255];
    } msg;
    
    void
    sendone(int fd_msg,int type,const char *str)
    {
        msg message;
    
        strcpy(message.str, str);
        size_t len = strlen(str) + 1;
    
        message.type = type;
    
        printf("Sending type %ld length %zu\n",message.type,len);
        if (msgsnd(fd_msg, &message, len, 0) == -1) {
            printf("Error sending message at message 1 queue %d --> %s\n", errno, strerror(errno));
            return;
        }
    
        printf("Message sent at the queue: %s\n", message.str);
    }
    
    void
    sender(int fd_msg)
    {
        char *string1 = "Hello";
        char *string2 = "World";
    
        sendone(fd_msg,2,string2);
        sendone(fd_msg,1,string1);
    
        return;
    }
    
    ssize_t
    recvone(int fd_msg,msg *message)
    {
        int type = 0;
    
        ssize_t len = msgrcv(fd_msg, message, sizeof(msg) - sizeof(long int), type, 0);
        if (len == -1) {
            printf("error receiving message %d --> %s\n", errno, strerror(errno));
            return len;
        }
    
        printf("Received type %ld message of length: %zu\n",message->type,len);
        printf("Message received! : %s\n",message->str);
    
        return len;
    }
    
    void
    receiver(int fd_msg)
    {
        msg message;
        char strings[2][255];
    
        sleep(1);
    
        // deliberately receive messages in any order
        for (int imsg = 1;  imsg <= 2;  ++imsg) {
            recvone(fd_msg,&message);
            strcpy(strings[message.type - 1],message.str);
        }
    
        for (int imsg = 1;  imsg <= 2;  ++imsg)
            printf("receiver: string %d: %s\n",imsg,strings[imsg - 1]);
    
        return;
    }
    
    int
    main(int argc, char *argv[])
    {
        int fd_msg = -1;
        int key_msg = -1;
        pid_t p1;
        pid_t p2;
    
        key_msg = ftok("../Example/in.txt", 55);
        if ((fd_msg = msgget(key_msg, IPC_CREAT | 0770)) == -1) {
            printf("Error creation message queue %d --> %s\n", errno, strerror(errno));
            return -1;
        }
    
        if ((p1 = fork()) == 0) {
            // child 1 (writer)
            sender(fd_msg);
            exit(0);
        }
    
        waitpid(p1, NULL, 0);
    
        if ((p2 = fork()) == 0) {
            // child 2 (reader)
            receiver(fd_msg);
            exit(0);
        }
    
        waitpid(p2, NULL, 0);
    
        msgctl(fd_msg, IPC_RMID, NULL);
        return 0;
    }
    

    Here is the program output:

    Sending type 2 length 6
    Message sent at the queue: World
    Sending type 1 length 6
    Message sent at the queue: Hello
    Received type 2 message of length: 6
    Message received! : World
    Received type 1 message of length: 6
    Message received! : Hello
    receiver: string 1: Hello
    receiver: string 2: World