C语言实现四窗口聊天
程序员文章站
2022-04-05 14:09:11
c语言实现四窗口聊天,供大家参考,具体内容如下为了练习前段时间学习的共享内存、管道、消息队列等进程同步机制,做了一个聊天小项目。项目描述:有4个进程,a进程和b进程负责通信,从标准输入读到的字符串通过...
c语言实现四窗口聊天,供大家参考,具体内容如下
为了练习前段时间学习的共享内存、管道、消息队列等进程同步机制,做了一个聊天小项目。
项目描述:
有4个进程,a进程和b进程负责通信,从标准输入读到的字符串通过管道发给对方,a1和b1进程负责显示,其中:
- a进程和b进程通过管道通信,a进程和a1进程通过共享内存通信,b进程和b1进程通过消息队列通信;
- a进程从标准输入读到的字符串后,放到管道和共享内存里,从管道中读到的字符串放到共享内存里,b进程从管道中拿到a放的字符串,a1进程到共享内存中拿到字符串,打印到屏幕上;
- b进程从标准输入读到的字符串发给a进程,同时通过消息队列发给b1进程,b1进程从消息队列中读出消息,打印到屏幕上;
- 退出时,在a和b任意一个进程中输入 crtl+c 或者 ctrl+\ ,四个进程会删除所有管道、共享内存、消息队列等资源,然后有序退出。
操作系统:ubuntu20.4
语言:c
编译器:gcc
func.h
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <time.h> #include <sys/mman.h> #include <fcntl.h> #include <sys/select.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/msg.h> #include <signal.h> #define args_check(argc, num){if(argc!=num){fprintf(stderr,"args error!\n"); return -1;}} #define error_check(ret,num,msg){if(ret == num){perror(msg); return -1;}}
a.c
//========= a窗口 =========== //1.从标准输入读取数据,通过有名管道发送给b窗口 //2.接收从b窗口发送过来的数据 //3.通过共享内存和信号量,将从b来的数据发送给a1 //=========================== #include <func.h> int chata(int shmid, int semid, char *p); int sndtoa1(int semid, int shmid, char *p, char *msg);//把要打印的消息发送给a1 void closeall();//把关闭消息发送出去,关闭共享内存和信号量集 void sigfunc(int signum);//新的2号和3号信号处理函数,如果在a窗口发生2号和3号信号就调用close函数 //全局变量,后面捕获到退出信号回收资源时使用 int semid; int shmid;//共享内存 char *p; int fdwrite; int fdread; int main() { //1.创建信号量集,如果有新消息就往共享内存中写,类似生产者 semid = semget(2000, 1, ipc_creat|0666); error_check(semid, -1, "a semget"); shmid = shmget(1000, 4096, ipc_creat|0666);//创建一个共享内存 error_check(shmid, -1, "shmget"); p = (char *)shmat(shmid, null, 0); signal(sigint, sigfunc); signal(sigquit, sigfunc); int ret = chata(shmid, semid, p); error_check(ret, -1, "run a");//检查是否成功打开通信窗口 return 0; } int chata(int shmid, int semid, char *p){ //成功运行返回1,否则返回-1 fdread = open("1.pipe", o_rdonly);//以只读模式打开管道1 error_check(fdread, -1, "open fdread");//检查是否成功打开 fdwrite = open("2.pipe", o_wronly);//以只写模式打开管道2 error_check(fdwrite, -1, "open fdwrite");//检查 setbuf(stdin, null); puts("=========== a ==========="); char buf[512] = {0}; fd_set rdset;//设置一个信箱,用来监控有没有读取到信息 while(1){ struct timeval timeout;//设置超时 timeout.tv_sec = 5; timeout.tv_usec = 15000000;//超过5秒没有接收到信息就是超时 fd_zero(&rdset);//初始化集合,清空信箱 fd_set(fdread, &rdset);//将要监听的管道1注册到集合中 fd_set(stdin_fileno, &rdset);//将要监听的标准输入注册到集合中 int tret = select(fdread + 1,&rdset,null,null,&timeout);//调用select进行监听 if(tret == 0){ puts("time out!"); } //select阻塞进程,任意一个fd就绪,解除阻塞 //解除阻塞,检查是谁就绪 if(fd_isset(fdread, &rdset)){ //如果是管道就绪,读取管道中的内容,发送给a1 memset(buf, 0, sizeof(buf));//清空buf中的内容,用来接收管道中的信息 int ret = read(fdread, buf, 1024);//将管道中的信息读取出来 if(ret == 0){ //如果另一端对管道的写先关闭了,退出聊天 sigfunc(2); break; } //获取从b来的消息的类型 int type = 0; sscanf(buf, "%*d %d", &type);//读取消息的类别,1类为正常,2类为关闭所有窗口 int snd_ret = 0; switch (type){ case 1: //如果是1号信息,通过共享内存直接把消息发送给a1 snd_ret = sndtoa1(shmid, semid, p, buf); error_check(snd_ret, -1, "sndtoa1"); break; case 2: //=====如果是从b发过来的2号信息,关闭所有窗口===== //向a1发送一个空的2号信号,让a1自己退出,然后自己再退出 sigfunc(2); exit(0); } } if(fd_isset(stdin_fileno, &rdset)){ //如果标准输入准备就绪,读取标准输入区的数据,标记为3号信号,发送给a1和b time_t localtm; time(&localtm);//获取当前时间 localtm += 8*3600; memset(buf, 0, sizeof(buf));//清空buf int ret = read(stdin_fileno, buf, 1024);//读取数据 if(ret == 0){ //如果在标准输入中读到了终止符,退出聊天窗口 puts("i quite."); break; } char sstoa1[1024] = {0};//用来拼接数据,发送给a1的数据 char sstob[1024] = {0};//用来拼接数据,发送给b的数据 sprintf(sstoa1, "%ld %d %s", localtm, 3, buf); //标注为三号信号发送给a1 sprintf(sstob, "%ld %d %s", localtm, 1, buf); //标注为1号信号发送给b sndtoa1(shmid, semid, p, sstoa1);//发送给a1 write(fdwrite, sstob, sizeof(sstob));//通过管道发送给b } } close(fdread); close(fdwrite); return 1;//程序成功运行结束,返回1 } int sndtoa1(int shmid, int semid, char *p, char *msg){ //使用共享内存和信号量给a1传递信息 //信号量集的操作,如果有新消息就往共享内存中写,类似生产者 struct sembuf v; v.sem_num = 0; v.sem_op = +1; v.sem_flg = sem_undo; semop(semid, &v, 1); /* int shmid = shmget(1000, 4096, ipc_creat|0666);//创建一个共享内存 */ error_check(shmid, -1, "shmget"); /* char *p = (char *)shmat(shmid, null, 0); */ memcpy(p, msg, strlen(msg));//向共享内存中写信息 return 1; } void closeall(){ //根据共享内存和信号量级的标识符,关闭并删除它们 write(fdwrite, "0 2 0", 5);//通过管道发送给b shmdt(p); shmctl(shmid, ipc_rmid, null); semctl(semid, ipc_rmid, 0); close(fdwrite); close(fdread); exit(0); } void sigfunc(int signum){ printf("bye bye.\n"); //捕捉2号和3号信号,发送关闭信息给a1,然后调用closeall sndtoa1(shmid, semid, p, "0 2 0");//发送给a1 usleep(500); closeall(); }
b.c
//========= b窗口 =========== //1.从标准输入读取数据,通过有名管道发送给a窗口 //2.接收从a窗口发送过来的数据 //3.通过共享内存和信号量,将从a来的数据发送给b1 //=========================== #include <func.h> //自定义一个消息结构体,用来和b1传递数据 typedef struct mymsg{ long mtype; char mtext[512]; }mymsg_t; int chatb(char *pipe1, char *pipe2); int sndtob1(int msqid, char *msg);//把从a来的消息发送给b1 void closeall(); void sigfunc(int signum); //全局变量,后面回收资源时要用到 int msqid; int fdwrite; int fdread; int main() { msqid = msgget(3000, ipc_creat|0666); error_check(msqid, -1, "b msgget"); //注册新的信号处理函数 signal(sigint, sigfunc); signal(sigquit, sigfunc); int ret = chatb("./1.pipe", "./2.pipe"); error_check(ret, -1, "run b"); return 0; } int chatb(char *pipe1, char *pipe2){ //通信窗口2,读管道2中的信息,向管道1写信息 fdwrite = open(pipe1, o_wronly); error_check(fdwrite, -1, "open pipe1"); fdread = open(pipe2, o_rdonly); error_check(fdread, -1, "open pipe2"); setbuf(stdin, null); puts("============ b ============"); char buf[512] = {0}; fd_set rdset;//设置集合,用来监听 while(1){ //利用集合设置阻塞 struct timeval timeout; timeout.tv_sec = 5; timeout.tv_usec = 15000000; fd_zero(&rdset);//初始化集合 fd_set(fdread, &rdset); fd_set(stdin_fileno, &rdset); int tret = select(fdread+1,&rdset,null,null,&timeout); if(tret == 0){ puts("time out!"); } //集合中有就绪的,检查是谁就绪,并进行相应的操作 if(fd_isset(fdread, &rdset)){ //如果是管道就绪,读取数据并发送给b1 memset(buf, 0, sizeof(buf)); int ret = read(fdread, buf, 1024); if(ret == 0){ sigfunc(2); break; } int type = 0;//用来存储消息的类型 sscanf(buf, "%*d %d", &type);//从消息中获取类型信息 //如果是2号信息,关闭所有窗口 //向b1发送关闭信号,然后回收消息队列,再自己结束 if(type == 2){ sigfunc(2); exit(0); } //如果是其他有效信息,发送给b1 int snd_ret = sndtob1(msqid, buf); error_check(snd_ret, -1, "b sndtob1"); } if(fd_isset(stdin_fileno, &rdset)){ //如果是标准输入区就绪,读取数据,分别发给a和b1 time_t localtm; time(&localtm);//获取当前时间 localtm += 8*3600; memset(buf, 0, sizeof(buf)); int ret = read(stdin_fileno, buf, 1024); if(ret == 0){ puts("i quite."); break; } //按照协议拼接数据并发送出去 char sstoa[1024] = {0};//发送给a的数据 sprintf(sstoa, "%ld %d %s", localtm, 1, buf); write(fdwrite, sstoa, sizeof(sstoa)); char sstob1[1024] = {0};//发送给b1的数据标注为3号 sprintf(sstob1, "%ld %d %s", localtm, 3, buf); sndtob1(msqid, sstob1); } } close(fdread); close(fdwrite); return 1;//程序成功运行结束,返回1 } int sndtob1(int msqid, char *msg){ //通过消息队列,把数据发送给b1 mymsg_t msgtob1;//创建一个消息结构体 msgtob1.mtype = 1; memset(msgtob1.mtext, 0, sizeof(msgtob1.mtext)); memcpy(msgtob1.mtext, msg, strlen(msg)); msgsnd(msqid, &msgtob1, strlen(msg), 0); return 1; } void closeall(){ msgctl(msqid, ipc_rmid, 0);//删除消息队列 close(fdwrite);//关闭管道 close(fdread); exit(0); } void sigfunc(int signum){ printf("bye bye.\n"); //通过消息队列,把关闭信息发送给b1,然后删除消息队列,然后自己退出 sndtob1(msqid, "0 2 0");//发送给b1关闭信号 write(fdwrite, "0 2 0", 5);//发送给a关闭信号 usleep(500);//睡一下,等b1先关闭 //捕获2号和3号信号,调用closeall函数 closeall(); }
a1.c
//========== a1 ========== //1.从共享内存中读取消息 //2.打印 int display(); #include <func.h> int main() { int ret = display(); error_check(ret, -1, "a1 display"); return 0; } int display(){ //1.从共享内存中读取数据 //没有消息就等待,有消息就读取,使用信号量集,类似消费者 //1.1 创建一个信号量集,如果共享内存中有数据就读取,如果共享内存中没有数据就阻塞 int semid = semget(2000, 1, ipc_creat|0666); error_check(semid, -1, "a1 semget"); semctl(semid, 0, setval, 0);//信号量初始值设为0 //设置信号量,测试并取资源,类似消费者操作 struct sembuf p; p.sem_num = 0; p.sem_op = -1; p.sem_flg = sem_undo; printf("=========== a1 ===========\n"); while(1){ semop(semid, &p, 1);//p操作,测试并取资源 int shmid = shmget(1000, 4096, ipc_creat|0666); error_check(shmid, -1, "a1 shmget"); char *p = (char *)shmat(shmid, null, 0);//连接共享内存 int type = 0; sscanf(p, "%*d %d", &type);//获取消息的属性,然后根据协议执行相应的操作 switch (type){ case 1: //从b来的消息 printf("<<<<<<<<< receive <<<<<<<<<<\n"); struct tm *ptm = null; time_t tmp = 0; char ss[512] = {0}; sscanf(p, "%ld", &tmp);//读取消息中的时间信息 sscanf(p, "%*d %*d %[^\n]", ss); ptm = gmtime(&tmp); printf("%4d-%02d-%02d %02d:%02d:%02d\n", ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec); puts(ss); printf("\n"); //清空共享内存中的数据 memset(p, 0, 4096); break; case 2: printf("bye bye.\n"); shmdt(p); shmctl(shmid, ipc_rmid, null); exit(0); break; case 3: printf(">>>>>>>>> send >>>>>>>>>>>\n"); struct tm *ptm3 = null; time_t tmp3 = 0; char ss3[512] = {0}; sscanf(p, "%ld", &tmp3);//读取消息中的时间信息 sscanf(p, "%*d %*d %[^\n]", ss3); ptm3 = gmtime(&tmp3); printf("%4d-%02d-%02d %02d:%02d:%02d\n", ptm3->tm_year+1900,ptm3->tm_mon+1,ptm3->tm_mday, ptm3->tm_hour, ptm3->tm_min, ptm3->tm_sec); puts(ss3); printf("\n"); //清空共享内存中的数据 memset(p, 0, 4096); break; default: printf("sonething wrong!\n"); } } }
b1.c
//========== b1 =========== //接收来自b的消息,并打印 #include <func.h> typedef struct mymsg{ long mtype; char mtext[512]; }mymsg_t; int display(); int main() { int ret = display(); error_check(ret, -1, "b1 display"); return 0; } int display(){ printf("=========== b1 ===========\n"); while(1){ //接收来自b的消息 int msqid = msgget(3000, ipc_creat|0666); error_check(msqid, -1, "b1 msgget"); mymsg_t msgfromb; memset(&msgfromb, 0, sizeof(msgfromb)); msgrcv(msqid, &msgfromb, sizeof(msgfromb.mtext), 1, 0); //1.如果是2类信号,退出 int type = 0; sscanf(msgfromb.mtext, "%*d %d", &type);//读取消息的属性,根据不同属性,执行相应的操作 switch (type){ case 1: //从b来的消息 printf("<<<<<<<<< receive <<<<<<<<<<\n"); struct tm *ptm = null; time_t tmp = 0; char ss[512] = {0}; sscanf(msgfromb.mtext, "%ld", &tmp);//读取消息中的时间信息 sscanf(msgfromb.mtext, "%*d %*d %[^\n]", ss); ptm = gmtime(&tmp); printf("%4d-%02d-%02d %02d:%02d:%02d\n", ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec); puts(ss); printf("\n"); //清空共享内存中的数据 break; case 2: //删除消息队列并退出 printf("bye bye.\n"); msgctl(msqid, ipc_rmid, null); exit(0); case 3: printf(">>>>>>>>> send >>>>>>>>>>>\n"); struct tm *ptm3 = null; time_t tmp3 = 0; char ss3[512] = {0}; sscanf(msgfromb.mtext, "%ld", &tmp3);//读取消息中的时间信息 sscanf(msgfromb.mtext, "%*d %*d %[^\n]", ss3); ptm3 = gmtime(&tmp3); printf("%4d-%02d-%02d %02d:%02d:%02d\n", ptm3->tm_year+1900,ptm3->tm_mon+1,ptm3->tm_mday, ptm3->tm_hour, ptm3->tm_min, ptm3->tm_sec); puts(ss3); printf("\n"); break; default: printf("something wrong!\n"); } } }
运行如下:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。