聊天系统的实现

发布时间:2025-12-10 11:48:31 浏览次数:28

实验要求
在linux系统用c/c++语言编写一个多用户的聊天室管理系统。主要功能:
1 能做到3个以上用户之间的聊天;
2 系统要有用户管理功能;
3 每个用户能管理自己的权限,比如 不接受信息,撤销已发的信息等;可以自己发挥;
4 聊天信息的保存,比如保存三天内的信息,或其他规定;
5 敏感词的过滤等等;
6 自己添加的其他功能。
所用技术主要有:
C语言的多进程编程;进程间的通信;文件处理等技术。
注意:在一个linux主机上的多个用户的聊天室管理系统。

实验代码

server.c#include<sys/types.h>#include<sys/shm.h>#include<sys/ipc.h>#include<unistd.h> #include<stdlib.h> #include<stdio.h>#include<sys/wait.h> #include<signal.h> #include<string.h>#include <stdbool.h>#include<time.h>#include <pthread.h>#define SHMKEY 75#define MSGMAXN 35 //最大消息数typedef struct{time_t t; //时间int pid; //进程号char text[15]; //文本内容}MSG; //消息typedef struct{int pid; //进程号bool state; //是否在线bool send_state; //是否被禁言}PROCESS; //进程信息typedef struct{int spid; //server进程号int psum; //进程总数int temp; //临时变量int msgnum; //消息数量PROCESS process[15];MSG msg[MSGMAXN];}MEM; //内存内容int shmid;MEM *addr; //共享存储区首地址void init() //初始化{printf("server pid:%d\n",getpid());shmid=shmget(SHMKEY,1024,0777|IPC_CREAT); //创建共享存储区system("ipcs -m");//相当于在SHELL下执行 ipcs -m命令,该命令是显示系统所有的共享内存 addr=(MEM*)shmat(shmid,0,0); //获取首地址(*addr).psum=0;(*addr).msgnum=0;(*addr).spid=getpid();}void check(int signo){printf("checking...\n");char s[20][15];memset(s,'\0',200);FILE *file;char *FILE_NAME=(char*)"sensitive_word.bin";file = fopen(FILE_NAME,"rb"); //以rb方式打开文件if (file == NULL) perror("errno");int len;fseek(file, 0, SEEK_END); //文件指针定位到文件末尾,偏移0个字节 len = ftell(file); //获取文件长度, ftell函数用于得到文件位置指针当前位置相对于文件首的偏移字节数 fseek(file,0,SEEK_SET); //注意将文件指针移回文件的开头 for (int i=0;i<len/15;i++)fread(s[i],15,1,file); //读取文件内容,并存入sint curidx =((*addr).msgnum) - 1;char *current=(*addr).msg[curidx].text; int n = len/15;for (int i=0;i<n;i++){if(strstr(current,s[i])!=NULL){printf("%d发送敏感词%s",(*addr).msg[curidx].pid,s[i]);strcpy((*addr).msg[curidx].text,"******\n"); //覆盖敏感词break;}}}void write_senwd(){printf("请输入敏感词\n");char *FILE_NAME = (char*)"sensitive_word.bin"; //敏感词词库文件FILE *file;char s[15];file = fopen(FILE_NAME,"wb"); //创建文件,并以wb方式打开if (file == NULL) perror("errno");int i; for (i=0;i<15;i++){fgets(s,15,stdin);if (strstr(s,"1#")!=NULL) break;fwrite(&s,15,1,file); //将敏感词写入文件}printf("写入敏感词成功\n\n");fclose(file);}void delete_senwd(){printf("\n请输入敏感词\n");char a[30][15];int senwdnum;for(senwdnum=0;senwdnum<30;senwdnum++){fgets(a[senwdnum],15,stdin);if (strstr(a[senwdnum],"2#")!=NULL) break;}char s[30][15];memset(s,'\0',200);FILE *file ,*file1;char *FILE_NAME=(char*)"sensitive_word.bin";file = fopen(FILE_NAME,"rb"); //以rb方式打开文件if (file == NULL) perror("errno");int len;fseek(file, 0, SEEK_END); //文件指针定位到文件末尾,偏移0个字节 len = ftell(file); //获取文件长度, ftell函数用于得到文件位置指针当前位置相对于文件首的偏移字节数 fseek(file,0,SEEK_SET); //注意将文件指针移回文件的开头 for (int i=0;i<len/15;i++)fread(s[i],15,1,file); //读取文件内容,并存入s fclose(file); remove(FILE_NAME); //删除原文件file1 = fopen(FILE_NAME,"wb"); //新建文件for(int i=0;i<len/15;i++)for(int j=0;j<senwdnum;j++)if(strstr(a[j],s[i]) == NULL)fwrite(&s[i],15,1,file1); printf("删除敏感词成功\n\n");fclose(file1);}void show_senwd(){char s[30][15];memset(s,'\0',200);FILE *file;char *FILE_NAME=(char*)"sensitive_word.bin";file = fopen(FILE_NAME,"rb"); //以rb方式打开文件if (file == NULL) perror("errno");int len;fseek(file, 0, SEEK_END); //文件指针定位到文件末尾,偏移0个字节 len = ftell(file); //获取文件长度, ftell函数用于得到文件位置指针当前位置相对于文件首的偏移字节数 fseek(file,0,SEEK_SET); //注意将文件指针移回文件的开头 for (int i=0;i<len/15;i++){fread(s[i],15,1,file); //读取文件内容,并存入sprintf("%d---%s",i+1,s[i]);} printf("\n");}void manage(){int psum=(*addr).psum;printf("\n所有进程及状态:\n");for (int i=0;i<psum;i++){printf("%d ",(*addr).process[i].pid);if ((*addr).process[i].state) printf("\t在线");else printf("\t离开");if ((*addr).process[i].send_state) printf("\t可发言\n");else printf("\t被禁言\n");}int ctrl;printf("\n输入管理信号:1--移除进程\n2--禁言\n3--解除禁言\n");scanf("%d",&ctrl);if (ctrl==1) {int n;printf("输入要移除的进程号:");scanf("%d",&n);kill(n,SIGUSR2); //向要移除的进程发送信号printf("\n进程%d已移除\n",n);}if (ctrl==2 ||ctrl==3) {int n;printf("输入进程号:");scanf("%d",&n);for(int i=0;i<psum;i++){if ((*addr).process[i].pid==n){if (ctrl==2) (*addr).process[i].send_state = 0;else (*addr).process[i].send_state = 1;break;} //改变进程是否被禁言状态}}}void signal_to_all(){int n = (*addr).psum;for (int i=0;i<n;i++){if ((*addr).process[i].state == 1) {kill((*addr).process[i].pid,16);}}} void *cleanmsg(void* arg){MSG *msg;time_t t;struct tm *p,*msg_p; //通过tm结构来获得日期和时间int i;while(1){time(&t); //此函数会返回从公元 1970 年1 月1 日的UTC 时间从0 时0 分0 秒算起到现在所经过的秒数。如果t 并非空指针的话,此函数也会将返回值存到t 指针所指的内存。for (i = 0;i<(*addr).msgnum;i++){double fl=difftime(t,(*addr).msg[i].t); //返回两个time_t型变量之间的时间间隔if (fl<=120.0) break; //时间差小于120s}if (i == 0){sleep(10);continue;} //没有可清除msglockf(1,1,0); //锁定屏幕输出/*将未过期的消息向内存区内消息的区域前移动*/msg = (MSG *)malloc((*addr).msgnum-i); //申请一块内存,大小是共享存储区内未过期消息的大小memcpy(msg,&((*addr).msg[i]),((*addr).msgnum-i)*sizeof(MSG)); //备份未过期消息memcpy((*addr).msg,msg,((*addr).msgnum-i)*sizeof(MSG)); //复制备份(*addr).msgnum=(*addr).msgnum-i; //改变消息数(*addr).temp=i; //清除的消息数signal_to_all(); //向所有在线进程发送信号lockf(1,0,0); //解锁.free(msg);sleep(10);}}void EXIT(){int psum=(*addr).psum;for (int i=0;i<psum;i++){kill((*addr).process[i].pid,SIGUSR2); }exit(0);}int main(){init();signal(SIGUSR1,check);printf("1#---添加敏感词\n2#---删除敏感词\n3#---显示敏感词\n4#---管理线程\n5#---退出\n");pthread_t id1;int err = pthread_create(&id1, NULL, cleanmsg, NULL);if (err != 0) printf("can't create thread 1: %d\n", err);char control[10]; //控制字符while (1){fgets(control,10,stdin);if (strstr(control,"1#")!=NULL) write_senwd();if (strstr(control,"2#")!=NULL) delete_senwd();if (strstr(control,"3#")!=NULL) show_senwd();if (strstr(control,"4#")!=NULL) manage();if (strstr(control,"5#")!=NULL) {EXIT();exit(0);}}shmctl(shmid,IPC_RMID,0); //撤消共享存储区,归还资源exit(0);}client.c#include <stdio.h>#include <stdlib.h>#include <string.h>#include <pthread.h>#include <unistd.h>#include <sys/ipc.h>#include <sys/msg.h>#include <sys/shm.h>#include <signal.h>#include <stdbool.h>#define SHMKEY 75#define MSGMAXN 35typedef struct{time_t t;int pid;char text[15];}MSG;typedef struct{int pid;bool state;bool send_state;}PROCESS;typedef struct{int spid;int psum;int temp;int msgnum;PROCESS process[15];MSG msg[MSGMAXN];}MEM;MEM *addr; //共享存储区首地址int shmid;int i1,i2; //i1是消息的写指针,i2是消息的读指针int inx=0; //进程信息process数组的相对号int recv_flag = 1; //是否接收消息void init() //初始化{time_t t;struct tm *p;time(&t);p = gmtime(&t);printf("%d:%d:%d %d: 你已进入群聊\n",p->tm_hour,p->tm_min,p->tm_sec,getpid());int shmid=shmget(SHMKEY,1024,0777); //打开共享存储区if (shmid == -1){perror("shmget"); //输出上一个函数错误的原因exit(-2);}//映射共享内存addr = (MEM*)shmat(shmid, 0, 0);if (addr == (void *)-1){perror("shmat");exit(-3);}lockf(1,1,0);(*addr).process[(*addr).psum].pid=getpid();(*addr).process[(*addr).psum].state=1;(*addr).process[(*addr).psum].send_state=1;inx=(*addr).psum;(*addr).psum++;int in1=(*addr).msgnum;strcpy((*addr).msg[in1].text,"进入群聊\n");time(&(*addr).msg[in1].t);(*addr).msg[in1].pid=getpid();(*addr).msgnum++;lockf(1,0,0);}void send(){char c[15];while (1){fgets(c,15,stdin); if (!(*addr).process[inx].send_state) {printf("你已被禁言\n消息发送失败\n");continue;} //被禁言,忽略发送内容,进入下一次循环if (strstr(c,"1#")!=NULL) {recv_flag = 0;continue;} //不接受消息if (strstr(c,"2#")!=NULL) {recv_flag = 1;continue;} //接收消息lockf(1,1,0);i1=(*addr).msgnum;strcpy((*addr).msg[i1].text,c);time(&(*addr).msg[i1].t);(*addr).msg[i1].pid=getpid();kill((*addr).spid,SIGUSR1); //向server发送信号,提醒server检查消息(*addr).msgnum++;lockf(1,0,0);}}void *recv(void* arg) //接收消息{i2=0;struct tm *p;while (1){ while (((*addr).msgnum) <= i2){sleep(5);}if (!recv_flag) {i2++;sleep(10);continue;} //不接收信息lockf(1,1,0);if ((*addr).msg[i2].pid==getpid()){lockf(1,0,0);(i2)++;continue;}p = gmtime(&(*addr).msg[i2].t);printf("%d:%d:%d ",p->tm_hour,p->tm_min,p->tm_sec);printf("%d: ",(*addr).msg[i2].pid);printf(" %s",(*addr).msg[i2].text);lockf(1,0,0);(i2)++;}}void pexit(int sig){lockf(1,1,0); (*addr).process[inx].state=0; //不在线状态int in1=(*addr).msgnum;time(&(*addr).msg[in1].t);(*addr).msg[in1].pid=getpid();if (sig==SIGUSR2) {strcpy((*addr).msg[in1].text,"已被踢出群聊\n");printf("你已被踢出群聊\n");}else {strcpy((*addr).msg[in1].text,"已退出群聊\n");printf("你已退出群聊\n");}(*addr).msgnum++;lockf(1,0,0);exit(0);}void func(int signo){i2 -= (*addr).temp; //减去删除的消息数}int main(){init();signal(SIGUSR2,pexit);signal(SIGINT,pexit);signal(16,func);pthread_t id1;int err = pthread_create(&id1, NULL, recv, NULL);if (err != 0) printf("can't create thread 1: %d\n", err);send();pthread_join(id1, 0); //等待线程结束return 0;}

实验截图

需要做网站?需要网络推广?欢迎咨询客户经理 13272073477