Linux进程间通信(IPC)编程实践(五)消息队列实现回射客户/服务器

在我的上一篇博文中,讲解了关于消息队列的msgsnd和msgrcv函数的使用,这里我们试着来实现一个回射客户/服务器。

基本框架如下:

对于客户端: 用进程的PID进行区分类型,发往服务器的类型(mtype)总是1,包含自己的PID,还有一行消息。

对于回射服务器端: 创建一个消息队列,指定键值是1234,服务器不停地接受类型是1的消息,解析出数据部分的pid(mtext的前四个字节)——回射即可。注意回射回来的时候就没有必要再加上pid了,mtext直接写数据就可以了。

可以用下面四句话概括:

1)server进程接收时指定msgtyp0, 从队首不断接收消息

2)server进程发送时, 将mtype指定为接收到的client进程的pid

3)client进程发送的时候, mtype指定为自己进程的pid

4)client进程接收时, 需要将msgtyp指定为自己进程的pid, 只接收消息类型为自己pid的消息

[cpp]  view plain  copy
  1. /*  Server  */  
  2.   
  3. #include<stdlib.h>  
  4. #include<sys/ipc.h>  
  5. #include<sys/msg.h>  
  6. #include<sys/types.h>  
  7. #include<unistd.h>  
  8. #include<errno.h>  
  9. #include<string.h>  
  10.   
  11. #define ERR_EXIT(m) \  
  12.     do { \  
  13.         perror(m); \  
  14.         exit(EXIT_FAILURE); \  
  15.     } while(0)  
  16.   
  17. #define MSGMAX 8192  
  18. struct msgbuf  
  19. {  
  20.     long mtype;   
  21.     char mtext[MSGMAX];  
  22. };  
  23.   
  24.   
  25. void echo_ser(int msgid)  
  26. {  
  27.     struct msgbuf msg;  
  28.     memset(&msg, 0, sizeof(msg));  
  29.     int nrcv ;  
  30.     while (1)  
  31.     {  
  32.         if ((nrcv = msgrcv(msgid, &msg, sizeof(msg.mtext), 1, 0)) < 0); //指定接受优先级为1的(msgtyp)   
  33.         int pid = *((int *)msg.mtext);  
  34.         fputs(msg.mtext + 4, stdout);  
  35.         msg.mtype = pid;  
  36.         msgsnd(msgid, &msg, nrcv, 0);  
  37.         memset(&msg, 0, sizeof(msg));  
  38.   
  39.     }  
  40. }  
  41.   
  42. int main(int argc, char *argv[])  
  43. {  
  44.     int msgid;  
  45.     msgid = msgget(1234, IPC_CREAT | 0666); //创建一个消息队列   
  46.     if (msgid == -1)  
  47.         ERR_EXIT("msgget");  
  48.   
  49.     echo_ser(msgid);  
  50.   
  51.   
  52.     return 0;  
  53. }  

[cpp]  view plain  copy
  1. /*  Client   */  
  2.    
  3. #include<stdio.h>  
  4. #include<stdlib.h>  
  5. #include<sys/ipc.h>  
  6. #include<sys/msg.h>  
  7. #include<sys/types.h>  
  8. #include<unistd.h>  
  9. #include<errno.h>  
  10. #include<string.h>  
  11.   
  12. #define ERR_EXIT(m) \  
  13.     do { \  
  14.         perror(m); \  
  15.         exit(EXIT_FAILURE); \  
  16.     } while(0)  
  17.   
  18. #define MSGMAX 8192  
  19.   
  20. struct msgbuf  
  21. {  
  22.     long mtype;  
  23.     char mtext[MSGMAX];  
  24. };  
  25.   
  26. void echo_cli(int msgid)  
  27. {  
  28.     int nrcv;  
  29.     int pid = getpid();  
  30.     struct msgbuf msg;  
  31.     memset(&msg, 0, sizeof(msg));  
  32.     msg.mtype = 1;  
  33.     *((int *)msg.mtext) = pid;  
  34.     while (fgets(msg.mtext + 4, MSGMAX, stdin) != NULL) //客户端输入信息   
  35.     {  
  36.   
  37.         if (msgsnd(msgid, &msg, MSGMAX, IPC_NOWAIT) < 0)  
  38.             ERR_EXIT("msgsnd");  
  39.   
  40.         memset(msg.mtext + 4, 0, MSGMAX - 4);  
  41.         if ((nrcv = msgrcv(msgid, &msg, MSGMAX, pid, 0)) < 0)  
  42.             ERR_EXIT("msgsnd");  
  43.         fputs(msg.mtext + 4, stdout);  
  44.         memset(msg.mtext + 4, 0, MSGMAX - 4);  
  45.   
  46.     }  
  47. }  
  48.   
  49. int main(int argc, char *argv[])  
  50. {  
  51.   
  52.     int msgid;  
  53.     msgid = msgget(1234, 0); //打开名为1234的消息队列   
  54.     if (msgid == -1)  
  55.         ERR_EXIT("msgget");  
  56.   
  57.     echo_cli(msgid);  
  58.   
  59.     return 0;  
  60. }  

但上述程序是存在死锁的风险的,当同时开了多个客户端,将队列写满了,此时服务器端想要写入就会阻塞,而因为客户端一旦发送了数据就阻塞等待服务器端回射类型为pid的消息,即队列的消息不会减少,此时就会形成死锁,互相等待。非阻塞方式发送也不行,因为队列已满,会发生EAGAIN错误。

对此问题我们的解决方法是采用多个消息队列:


即某个客户端先创建一个私有消息队列,然后将私有消息队列标识符和具体数据通过共享的队列发送给Server,服务器fork 出一个子进程,此时根据私有队列标识符就可以将数据回射到这个队列,这个客户端就可以从私有队列读取到回射的数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值