QQ登录

只需要一步,快速开始

APP扫码登录

只需要一步,快速开始

手机号码,快捷登录

查看: 1831|回复: 0

[C/C++/Qt] Ubuntu四窗口聊天(gcc)

[复制链接]

等级头衔

积分成就    金币 : 2810
   泡泡 : 1516
   精华 : 6
   在线时间 : 1245 小时
   最后登录 : 2024-5-18

丰功伟绩

优秀达人突出贡献荣誉管理论坛元老

联系方式
发表于 2021-6-15 07:53:01 | 显示全部楼层 |阅读模式
项目描述:
; H& |0 O; a2 |) W( K       有4个进程,A进程和B进程负责通信,从标准输入读到的字符串通过管道发给对方,A1和B1进程负责显示,其中:+ f7 r" }% v  ]
  • A进程和B进程通过管道通信,A进程和A1进程通过共享内存通信,B进程和B1进程通过消息队列通信;
  • A进程从标准输入读到的字符串后,放到管道和共享内存里,从管道中读到的字符串放到共享内存里,B进程从管道中拿到A放的字符串,A1进程到共享内存中拿到字符串,打印到屏幕上;
  • B进程从标准输入读到的字符串发给A进程,同时通过消息队列发给B1进程,B1进程从消息队列中读出消息,打印到屏幕上;
  • 退出时,在A和B任意一个进程中输入 Crtl+c 或者 Ctrl+\ ,四个进程会删除所有管道、共享内存、消息队列等资源,然后有序退出。( O& ]! V2 _, i' U; s! N: s
操作系统:Ubuntu20.4
+ b/ w( |6 ^9 U4 `语言:c: T: q# K6 {3 v9 }$ F% [3 N
编译器:gcc2 ~6 e: |: e6 j
func.h9 A& A  ?' v7 X' z" b5 D
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <sys/types.h>
  5. #include <unistd.h>
  6. #include <time.h>
  7. #include <sys/mman.h>
  8. #include <fcntl.h>
  9. #include <sys/select.h>
  10. #include <sys/shm.h>
  11. #include <sys/sem.h>
  12. #include <sys/msg.h>
  13. #include <signal.h>
  14. #define ARGS_CHECK(argc, num){if(argc!=num){fprintf(stderr,"args error!\n"); return -1;}}
  15. #define ERROR_CHECK(ret,num,msg){if(ret == num){perror(msg); return -1;}}
a.c# g" S" |( [& g. o; O, g' i# J
  1. //========= A窗口 ===========
  2. //1.从标准输入读取数据,通过有名管道发送给B窗口
  3. //2.接收从B窗口发送过来的数据
  4. //3.通过共享内存和信号量,将从B来的数据发送给A1
  5. //===========================
  6. #include <func.h>
  7. int chatA(int shmid, int semid, char *p);
  8. int sndToA1(int semid, int shmid, char *p, char *msg);//把要打印的消息发送给A1
  9. void closeAll();//把关闭消息发送出去,关闭共享内存和信号量集
  10. void sigFunc(int signum);//新的2号和3号信号处理函数,如果在A窗口发生2号和3号信号就调用close函数
  11. //全局变量,后面捕获到退出信号回收资源时使用
  12. int semid;
  13. int shmid;//共享内存
  14. char *p;
  15. int fdWrite;
  16. int fdRead;
  17. int main()
  18. {
  19.     //1.创建信号量集,如果有新消息就往共享内存中写,类似生产者
  20.     semid = semget(2000, 1, IPC_CREAT|0666);
  21.     ERROR_CHECK(semid, -1, "A semget");
  22.     shmid = shmget(1000, 4096, IPC_CREAT|0666);//创建一个共享内存
  23.     ERROR_CHECK(shmid, -1, "shmget");
  24.     p = (char *)shmat(shmid, NULL, 0);
  25.     signal(SIGINT, sigFunc);
  26.     signal(SIGQUIT, sigFunc);
  27.     int ret = chatA(shmid, semid, p);
  28.     ERROR_CHECK(ret, -1, "run A");//检查是否成功打开通信窗口
  29.     return 0;
  30. }
  31. int chatA(int shmid, int semid, char *p){
  32.     //成功运行返回1,否则返回-1
  33.     fdRead = open("1.pipe", O_RDONLY);//以只读模式打开管道1
  34.     ERROR_CHECK(fdRead, -1, "open fdRead");//检查是否成功打开
  35.     fdWrite = open("2.pipe", O_WRONLY);//以只写模式打开管道2
  36.     ERROR_CHECK(fdWrite, -1, "open fdWrite");//检查
  37.     setbuf(stdin, NULL);
  38.     puts("=========== A ===========");
  39.     char buf[512] = {0};
  40.     fd_set rdset;//设置一个信箱,用来监控有没有读取到信息
  41.     while(1){
  42.         struct timeval timeout;//设置超时
  43.         timeout.tv_sec = 5;
  44.         timeout.tv_usec = 15000000;//超过5秒没有接收到信息就是超时
  45.         FD_ZERO(&rdset);//初始化集合,清空信箱
  46.         FD_SET(fdRead, &rdset);//将要监听的管道1注册到集合中
  47.         FD_SET(STDIN_FILENO, &rdset);//将要监听的标准输入注册到集合中
  48.         int tret = select(fdRead + 1,&rdset,NULL,NULL,&timeout);//调用select进行监听
  49.         if(tret == 0){
  50.             puts("time out!");
  51.         }
  52.         //select阻塞进程,任意一个FD就绪,解除阻塞
  53.         //解除阻塞,检查是谁就绪
  54.         if(FD_ISSET(fdRead, &rdset)){
  55.             //如果是管道就绪,读取管道中的内容,发送给A1
  56.             memset(buf, 0, sizeof(buf));//清空buf中的内容,用来接收管道中的信息
  57.             int ret = read(fdRead, buf, 1024);//将管道中的信息读取出来
  58.             if(ret == 0){
  59.                 //如果另一端对管道的写先关闭了,退出聊天
  60.                 sigFunc(2);
  61.                 break;
  62.             }
  63.             //获取从B来的消息的类型
  64.             int type = 0;
  65.             sscanf(buf, "%*d %d", &type);//读取消息的类别,1类为正常,2类为关闭所有窗口
  66.             int snd_ret = 0;
  67.             
  68.             switch (type){
  69.             case 1:
  70.                 //如果是1号信息,通过共享内存直接把消息发送给A1
  71.                 snd_ret = sndToA1(shmid, semid, p, buf);
  72.                 ERROR_CHECK(snd_ret, -1, "sndToA1");
  73.                 break;
  74.             case 2:
  75.                 //=====如果是从B发过来的2号信息,关闭所有窗口=====
  76.                 //向A1发送一个空的2号信号,让A1自己退出,然后自己再退出
  77.                 sigFunc(2);
  78.                 exit(0);
  79.             }
  80.         }
  81.         if(FD_ISSET(STDIN_FILENO, &rdset)){
  82.             //如果标准输入准备就绪,读取标准输入区的数据,标记为3号信号,发送给A1和B
  83.             time_t localtm;
  84.             time(&localtm);//获取当前时间
  85.             localtm += 8*3600;
  86.             memset(buf, 0, sizeof(buf));//清空buf
  87.             int ret = read(STDIN_FILENO, buf, 1024);//读取数据
  88.             if(ret == 0){
  89.                 //如果在标准输入中读到了终止符,退出聊天窗口
  90.                 puts("I quite.");
  91.                 break;
  92.             }
  93.             char sstoA1[1024] = {0};//用来拼接数据,发送给A1的数据
  94.             char sstoB[1024] = {0};//用来拼接数据,发送给B的数据
  95.             sprintf(sstoA1, "%ld %d %s", localtm, 3, buf); //标注为三号信号发送给A1
  96.             sprintf(sstoB, "%ld %d %s", localtm, 1, buf); //标注为1号信号发送给B
  97.             sndToA1(shmid, semid, p, sstoA1);//发送给A1
  98.             write(fdWrite, sstoB, sizeof(sstoB));//通过管道发送给B
  99.         }
  100.     }
  101.     close(fdRead);
  102.     close(fdWrite);
  103.     return 1;//程序成功运行结束,返回1
  104. }
  105. int sndToA1(int shmid, int semid, char *p, char *msg){
  106.     //使用共享内存和信号量给A1传递信息
  107.     //信号量集的操作,如果有新消息就往共享内存中写,类似生产者
  108.     struct sembuf V;
  109.     V.sem_num = 0;
  110.     V.sem_op = +1;
  111.     V.sem_flg = SEM_UNDO;
  112.     semop(semid, &V, 1);
  113.     /* int shmid = shmget(1000, 4096, IPC_CREAT|0666);//创建一个共享内存 */
  114.     ERROR_CHECK(shmid, -1, "shmget");
  115.     /* char *p = (char *)shmat(shmid, NULL, 0); */
  116.     memcpy(p, msg, strlen(msg));//向共享内存中写信息
  117.     return 1;
  118. }
  119. void closeAll(){
  120.     //根据共享内存和信号量级的标识符,关闭并删除它们
  121.     write(fdWrite, "0 2 0", 5);//通过管道发送给B
  122.     shmdt(p);
  123.     shmctl(shmid, IPC_RMID, NULL);
  124.     semctl(semid, IPC_RMID, 0);
  125.     close(fdWrite);
  126.     close(fdRead);
  127.     exit(0);
  128. }
  129. void sigFunc(int signum){
  130.     printf("Bye Bye.\n");
  131.     //捕捉2号和3号信号,发送关闭信息给A1,然后调用closeAll
  132.     sndToA1(shmid, semid, p, "0 2 0");//发送给A1
  133.     usleep(500);
  134.     closeAll();
  135. }
b.c
0 D6 H8 ]  u* F, ?
  1. //========= B窗口 ===========
  2. //1.从标准输入读取数据,通过有名管道发送给A窗口
  3. //2.接收从A窗口发送过来的数据
  4. //3.通过共享内存和信号量,将从A来的数据发送给B1
  5. //===========================
  6. #include <func.h>
  7. //自定义一个消息结构体,用来和B1传递数据
  8. typedef struct myMsg{
  9.     long mtype;
  10.     char mtext[512];
  11. }myMsg_t;
  12. int chatB(char *pipe1, char *pipe2);
  13. int sndToB1(int msqid, char *msg);//把从A来的消息发送给B1
  14. void closeAll();
  15. void sigFunc(int signum);
  16. //全局变量,后面回收资源时要用到
  17. int msqid;
  18. int fdWrite;
  19. int fdRead;
  20. int main()
  21. {
  22.     msqid = msgget(3000, IPC_CREAT|0666);
  23.     ERROR_CHECK(msqid, -1, "B msgget");
  24.     //注册新的信号处理函数
  25.     signal(SIGINT, sigFunc);
  26.     signal(SIGQUIT, sigFunc);
  27.     int ret = chatB("./1.pipe", "./2.pipe");
  28.     ERROR_CHECK(ret, -1, "run B");
  29.     return 0;
  30. }
  31. int chatB(char *pipe1, char *pipe2){
  32.     //通信窗口2,读管道2中的信息,向管道1写信息
  33.     fdWrite = open(pipe1, O_WRONLY);
  34.     ERROR_CHECK(fdWrite, -1, "open pipe1");
  35.     fdRead = open(pipe2, O_RDONLY);
  36.     ERROR_CHECK(fdRead, -1, "open pipe2");
  37.     setbuf(stdin, NULL);
  38.     puts("============ B ============");
  39.     char buf[512] = {0};
  40.     fd_set rdset;//设置集合,用来监听
  41.     while(1){
  42.         //利用集合设置阻塞
  43.         struct timeval timeout;
  44.         timeout.tv_sec = 5;
  45.         timeout.tv_usec = 15000000;
  46.         FD_ZERO(&rdset);//初始化集合
  47.         FD_SET(fdRead, &rdset);
  48.         FD_SET(STDIN_FILENO, &rdset);
  49.         int tret = select(fdRead+1,&rdset,NULL,NULL,&timeout);
  50.         if(tret == 0){
  51.             puts("time out!");
  52.         }
  53.         //集合中有就绪的,检查是谁就绪,并进行相应的操作
  54.         if(FD_ISSET(fdRead, &rdset)){
  55.             //如果是管道就绪,读取数据并发送给B1
  56.             memset(buf, 0, sizeof(buf));
  57.             int ret = read(fdRead, buf, 1024);
  58.             if(ret == 0){
  59.                 sigFunc(2);
  60.                 break;
  61.             }
  62.             int type = 0;//用来存储消息的类型
  63.             sscanf(buf, "%*d %d", &type);//从消息中获取类型信息
  64.             //如果是2号信息,关闭所有窗口
  65.             //向B1发送关闭信号,然后回收消息队列,再自己结束
  66.             if(type == 2){
  67.                 sigFunc(2);
  68.                 exit(0);
  69.             }
  70.             //如果是其他有效信息,发送给B1
  71.             int snd_ret = sndToB1(msqid, buf);
  72.             ERROR_CHECK(snd_ret, -1, "B sndToB1");
  73.         }
  74.         if(FD_ISSET(STDIN_FILENO, &rdset)){
  75.             //如果是标准输入区就绪,读取数据,分别发给A和B1
  76.             time_t localtm;
  77.             time(&localtm);//获取当前时间
  78.             localtm += 8*3600;
  79.             memset(buf, 0, sizeof(buf));
  80.             int ret = read(STDIN_FILENO, buf, 1024);
  81.             if(ret == 0){
  82.                 puts("I quite.");
  83.                 break;
  84.             }
  85.             //按照协议拼接数据并发送出去
  86.             char sstoA[1024] = {0};//发送给A的数据
  87.             sprintf(sstoA, "%ld %d %s", localtm, 1, buf);
  88.             write(fdWrite, sstoA, sizeof(sstoA));
  89.             char sstoB1[1024] = {0};//发送给B1的数据标注为3号
  90.             sprintf(sstoB1, "%ld %d %s", localtm, 3, buf);
  91.             sndToB1(msqid, sstoB1);
  92.         }
  93.     }
  94.     close(fdRead);
  95.     close(fdWrite);
  96.     return 1;//程序成功运行结束,返回1
  97. }
  98. int sndToB1(int msqid, char *msg){
  99.     //通过消息队列,把数据发送给B1
  100.     myMsg_t msgtoB1;//创建一个消息结构体
  101.     msgtoB1.mtype = 1;
  102.     memset(msgtoB1.mtext, 0, sizeof(msgtoB1.mtext));
  103.     memcpy(msgtoB1.mtext, msg, strlen(msg));
  104.     msgsnd(msqid, &msgtoB1, strlen(msg), 0);
  105.     return 1;
  106. }
  107. void closeAll(){
  108.     msgctl(msqid, IPC_RMID, 0);//删除消息队列
  109.     close(fdWrite);//关闭管道
  110.     close(fdRead);
  111.     exit(0);
  112. }
  113. void sigFunc(int signum){
  114.     printf("Bye Bye.\n");
  115.     //通过消息队列,把关闭信息发送给B1,然后删除消息队列,然后自己退出
  116.     sndToB1(msqid, "0 2 0");//发送给B1关闭信号
  117.     write(fdWrite, "0 2 0", 5);//发送给A关闭信号
  118.     usleep(500);//睡一下,等B1先关闭
  119.     //捕获2号和3号信号,调用closeAll函数
  120.     closeAll();
  121. }
a1.c& H" i; E# B5 @- q# w' X/ _
  1. //========== A1 ==========
  2. //1.从共享内存中读取消息
  3. //2.打印
  4. int display();
  5. #include <func.h>
  6. int main()
  7. {
  8.     int ret = display();
  9.     ERROR_CHECK(ret, -1, "A1 display");
  10.     return 0;
  11. }
  12. int display(){
  13.     //1.从共享内存中读取数据
  14.     //没有消息就等待,有消息就读取,使用信号量集,类似消费者
  15.     //1.1 创建一个信号量集,如果共享内存中有数据就读取,如果共享内存中没有数据就阻塞
  16.     int semid = semget(2000, 1, IPC_CREAT|0666);
  17.     ERROR_CHECK(semid, -1, "A1 semget");
  18.     semctl(semid, 0, SETVAL, 0);//信号量初始值设为0
  19.     //设置信号量,测试并取资源,类似消费者操作
  20.     struct sembuf P;
  21.     P.sem_num = 0;
  22.     P.sem_op = -1;
  23.     P.sem_flg = SEM_UNDO;
  24.     printf("=========== A1 ===========\n");
  25.     while(1){
  26.         semop(semid, &P, 1);//P操作,测试并取资源
  27.         int shmid = shmget(1000, 4096, IPC_CREAT|0666);
  28.         ERROR_CHECK(shmid, -1, "A1 shmget");
  29.         char *p = (char *)shmat(shmid, NULL, 0);//连接共享内存
  30.         int type = 0;
  31.         sscanf(p, "%*d %d", &type);//获取消息的属性,然后根据协议执行相应的操作
  32.         switch (type){
  33.         case 1:
  34.             //从B来的消息
  35.             printf("<<<<<<<<< receive <<<<<<<<<<\n");
  36.             struct tm *ptm = NULL;
  37.             time_t tmp = 0;
  38.             char ss[512] = {0};
  39.             sscanf(p, "%ld", &tmp);//读取消息中的时间信息
  40.             sscanf(p, "%*d %*d %[^\n]", ss);
  41.             ptm = gmtime(&tmp);
  42.             printf("%4d-%02d-%02d %02d:%02d:%02d\n",
  43.                    ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,
  44.                    ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
  45.             puts(ss);
  46.             printf("\n");
  47.             //清空共享内存中的数据
  48.             memset(p, 0, 4096);
  49.             break;
  50.         case 2:
  51.             printf("Bye Bye.\n");
  52.             shmdt(p);
  53.             shmctl(shmid, IPC_RMID, NULL);
  54.             exit(0);
  55.             break;
  56.         case 3:
  57.             printf(">>>>>>>>>  send  >>>>>>>>>>>\n");
  58.             struct tm *ptm3 = NULL;
  59.             time_t tmp3 = 0;
  60.             char ss3[512] = {0};
  61.             sscanf(p, "%ld", &tmp3);//读取消息中的时间信息
  62.             sscanf(p, "%*d %*d %[^\n]", ss3);
  63.             ptm3 = gmtime(&tmp3);
  64.             printf("%4d-%02d-%02d %02d:%02d:%02d\n",
  65.                    ptm3->tm_year+1900,ptm3->tm_mon+1,ptm3->tm_mday,
  66.                    ptm3->tm_hour, ptm3->tm_min, ptm3->tm_sec);
  67.             puts(ss3);
  68.             printf("\n");
  69.             //清空共享内存中的数据
  70.             memset(p, 0, 4096);
  71.             break;
  72.         default:
  73.             printf("sonething wrong!\n");
  74.         }
  75.     }
  76. }
b1.c
$ Z' ?' r' ?8 W7 B
  1. //========== B1 ===========
  2. //接收来自B的消息,并打印
  3. #include <func.h>
  4. typedef struct myMsg{
  5.     long mtype;
  6.     char mtext[512];
  7. }myMsg_t;
  8. int display();
  9. int main()
  10. {
  11.     int ret = display();
  12.     ERROR_CHECK(ret, -1, "B1 display");
  13.     return 0;
  14. }
  15. int display(){
  16.     printf("=========== B1 ===========\n");
  17.     while(1){
  18.         //接收来自B的消息
  19.         int msqid = msgget(3000, IPC_CREAT|0666);
  20.         ERROR_CHECK(msqid, -1, "B1 msgget");
  21.         myMsg_t msgfromB;
  22.         memset(&msgfromB, 0, sizeof(msgfromB));
  23.         msgrcv(msqid, &msgfromB, sizeof(msgfromB.mtext), 1, 0);
  24.         //1.如果是2类信号,退出
  25.         int type = 0;
  26.         sscanf(msgfromB.mtext, "%*d %d", &type);//读取消息的属性,根据不同属性,执行相应的操作
  27.         switch (type){
  28.         case 1:
  29.             //从B来的消息
  30.             printf("<<<<<<<<< receive <<<<<<<<<<\n");
  31.             struct tm *ptm = NULL;
  32.             time_t tmp = 0;
  33.             char ss[512] = {0};
  34.             sscanf(msgfromB.mtext, "%ld", &tmp);//读取消息中的时间信息
  35.             sscanf(msgfromB.mtext, "%*d %*d %[^\n]", ss);
  36.             ptm = gmtime(&tmp);
  37.             printf("%4d-%02d-%02d %02d:%02d:%02d\n",
  38.                    ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,
  39.                    ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
  40.             puts(ss);
  41.             printf("\n");
  42.             //清空共享内存中的数据
  43.             break;
  44.         case 2:
  45.             //删除消息队列并退出
  46.             printf("Bye Bye.\n");
  47.             msgctl(msqid, IPC_RMID, NULL);
  48.             exit(0);
  49.         case 3:
  50.             printf(">>>>>>>>>  send  >>>>>>>>>>>\n");
  51.             struct tm *ptm3 = NULL;
  52.             time_t tmp3 = 0;
  53.             char ss3[512] = {0};
  54.             sscanf(msgfromB.mtext, "%ld", &tmp3);//读取消息中的时间信息
  55.             sscanf(msgfromB.mtext, "%*d %*d %[^\n]", ss3);
  56.             ptm3 = gmtime(&tmp3);
  57.             printf("%4d-%02d-%02d %02d:%02d:%02d\n",
  58.                    ptm3->tm_year+1900,ptm3->tm_mon+1,ptm3->tm_mday,
  59.                    ptm3->tm_hour, ptm3->tm_min, ptm3->tm_sec);
  60.             puts(ss3);
  61.             printf("\n");
  62.             break;
  63.         default:
  64.             printf("Something wrong!\n");
  65.         }
  66.     }
  67. }
运行如下:
6 ^1 r6 \2 d  V. d! W 1.jpg
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|paopaomj.COM ( 渝ICP备18007172号 )

GMT+8, 2024-5-18 21:31

Powered by paopaomj X3.4 © 2016-2024 sitemap

快速回复 返回顶部 返回列表