Linux实训《单机版聊天室》源码,答辩ppt,实习计划书,实习报告

https://download.csdn.net/download/weixin_43042683/86949831?spm=1001.2014.3001.5503

一、综合训练任务

本次实习设计的单主机聊天室需要满足以下功能需求:

(1)提供客户端和服务端,所有用户通过客户端进行聊天,每个用户可以看到所有聊天信息。

(2)要求所有登录的系统用户可以登录服务器进行聊天,服务器为守护进程。

(3)使用消息队列完成。服务器启动后使用特定的key或目录(将目录转换为key)建立消息队列,监听客户程序发出的消息(登录消息、退出消息、聊天消息)并完成消息的转发,对特定信息进行捕获来退出服务,退出服务是要通知客户端退出;客户端程序启动时检查特定key的消息队列是否建立,并通过位置参数获取用户名,若消息队列没有建立,则发送登录消息,然后准备发送消息,同事监听消息队列读取服务程序转发的聊天消息,当用户发出特定的字符时退出聊天。

二、总体设计

单机聊天室采用客户端,服务器的模式。通过消息队列存储客户端之间的信息交流,所有用户可以登录服务器进入聊天室参与聊天,服务器采用守护进程。

客户端的功能如下:

  1. 客户端之间可以相互收发消息
  2. 能收到进入聊天室的通知消息
  3. 离开聊天室的通知消息
  4. 每条消息都有时间记录

服务器功能如下:

  1. 广播将所有通知消息转发给所有用户
  2. 完成客户端之间的消息转发
  3. 解散聊天室,杀死所有进程
  4. 记录客户端进出服务器的信息

三、详细设计说明

客户端功能

设计如下:

1,客户端之间的收发消息通过子进程读消息,父进程发消息来实现。发消息时通过msgsnd函数将包含消息的结构体加入消息队列,收消息时通过msgrcv函数读取消息队列信息。

fgets(msg.mtext,MSG_SIZE,stdin);struct timeval tv;gettimeofday(&tv, NULL);t = tv.tv_sec;struct tm*p_time = localtime(&t);strftime(str, 26, "%Y-%m-%d %H:%M:%S", p_time);strcpy(msg.msg_time, str);//记录时间信息msgsnd(msgid,&msg,MSG_LEN,0);//发送结构体

以上代码实现消息的发送功能,在结构体发送给消息队列时记录发送时间,达到对消息计时的功能。

2,客户端收到新用户加入和离开聊天室的信息通过服务器的广播功能实现,用户进入和离开聊天室时将进入、离开信息发送给消息队列,服务器将这些消息广播给所有客户端。

if(strncmp(msg.mtext,"exit",4)==0){msg.subtype = 3;struct timeval tv; gettimeofday(&tv, NULL);t = tv.tv_sec;struct tm*p_time = localtime(&t);strftime(str, 26, "%Y-%m-%d %H:%M:%S", p_time);strcpy(msg.msg_time, str);//记录时间信息msgsnd(msgid,&msg,MSG_LEN,0);//发送退出信息fflush(stdout);//刷新缓冲区sleep(2);kill(pid,SIGKILL);exit(0);}

以上代码实现离开聊天室通知所有客户端的功能,将包含客户端退出聊天室的结构体发送给消息队列后,必须要刷新缓冲区,不然不能及时输出客户端的退出信息。

3,消息的时间记录,通过Linux中时间结构体来记录,在发送结构体给消息队列时及时记录时间信息。

服务器设

计功能如下:

服务器生成一个采用链表的形式生成消息队列链表结构如下:

struct msgbuf{long mtype;int subtype;    //消息请求类型int pid;    //发送消息进程号char user_name[MSG_SIZE];char mtext[MSG_SIZE];char msg_time[MSG_SIZE];
}msg;

结构体中包含进程号、消息请求类型(设计时采用switch选择语句实现消息的不同的类型便于扩展功能)、内容信息、时间信息。

1,广播消息时遍历消息队列,给每个进程发送消息。

int i;for(i=0 ;i<Length(head) ;i++){msg->mtype = GetValue(head,i);if(msg->mtype != msg->pid){//mtype = pid排除将广播消息发送给发消息的进程msgsnd(msgid,msg,MSG_LEN,0);}}

2,解散聊天室杀死进程,在服务器接收到quit消息时杀死所有进程,删除消息队列,以此实现解散聊天室的功能,删除消息队列和杀死进程的代码如下:

void Delete(LinkList head,int value)
{LinkList p = head->next;while(p!=NULL){if(p->data == value){head->next = p->next;free(p);p = head->next;}else{head = p;p = p->next;}}
}

以上代码实现对链表的删除

if(strncmp(msg.mtext,"quit",4)==0){msgsnd(msgid,&msg,MSG_LEN,0);sleep(2);msgctl(msgid,IPC_RMID,NULL);//删除消息队列kill(pid,SIGKILL);  //杀死子进程exit(0);}

以上代码杀死所有进程,实现模拟解散聊天室的功能,此代码虽然实现了对进程的杀死但是并没有按照本次实习的要求处理进程信号,是按照系统默认杀死进程,实习要求应该是对退出信号进行处理,在答辩时发现这个问题,所以这个单机聊天室没能完全按照要求实现,这也提醒我在今后的实习或是工作中一定要仔细审题和老师多交流,避免这种问题发生。

四、调试与测试

1.调试时首先启动服务器,直接登录客户端会提示服务器没有开启,如图所示:

2.启动服务器后客户端可以登录服务器进入聊天室。

3. 客户端登录后相互之间通过服务器的转发实现聊天功能,客户端之间的聊天如下图

4.在客户端输入exit后退出聊天室,服务器会收集客户端的提示信息并且通知所有在聊天室中的客户端有客户端退出聊天室。

5.在服务器输入quit退出服务器,这时服务器会通知所有进程服务器即将关闭,然后杀死所有进程并且删除消息队列,模拟解散聊天室;此功能的不足时没能对退出消息捕获处理而是按照系统默认处理,这不符合实际,还需优化。

五、附录:核心代码清单

以下代码是客户端子进程和父进程的相关代码实现了消息的收发。

if((pid = fork())==-1){perror("fork error.");//创建进程失败exit(1);}else if(pid == 0){  //子进程负责读while(1){msgrcv(msgid,&msg,MSG_LEN,TYPE_ME,0);//判断接收到的消息是否为quitif(strncmp(msg.mtext,"quit",4) == 0){printf("\33[36m-------------------------服务器即将关闭-----------------------\33[0m\n");kill(TYPE_ME,SIGUSR1); //杀死所有进程exit(0);}//检测用户是否输入exit退出操作 if(strncmp(msg.mtext,"exit",4) == 0){msg.subtype = 3;}switch(msg.subtype){case 1:printf("\033[33m\n%s\n\033[0m",msg.msg_time);//显示进入聊天室时间printf("\033[33m[%s 进入聊天室]\033[0m\n\n",msg.user_name);break;case 2:printf("\033[33m\n%s\n\033[0m", msg.msg_time);//显示发送消息的时间printf("\033[33m[%s]:\033[0m %s\n",msg.user_name,msg.mtext);break;case 3:printf("\033[33m\n%s\n\033[0m", msg.msg_time);//显示退出聊天室时间printf("\033[33m[%s 退出聊天室]\033[0m\n\n",msg.user_name);break;default :break;}}}else{   //父进程用于发送消息msg.mtype = MSG_TO_SERVER;msg.subtype = 2;//广播while(1){fgets(msg.mtext,MSG_SIZE,stdin);struct timeval tv;//生成当前时间信息gettimeofday(&tv, NULL);t = tv.tv_sec;struct tm*p_time = localtime(&t);strftime(str, 26, "%Y-%m-%d %H:%M:%S", p_time);strcpy(msg.msg_time, str);msgsnd(msgid,&msg,MSG_LEN,0);//结构体进入消息队列//当此进程输入exit时,表示进程推出聊天//由服务器通知其他进程,该进程已下线if(strncmp(msg.mtext,"exit",4)==0){msg.subtype = 3;struct timeval tv;//生成当前时间信息gettimeofday(&tv, NULL);t = tv.tv_sec;struct tm*p_time = localtime(&t);strftime(str, 26, "%Y-%m-%d %H:%M:%S", p_time);strcpy(msg.msg_time, str);msgsnd(msgid,&msg,MSG_LEN,0);fflush(stdout);//刷新缓冲区输出信息,如果不刷新,其他终端不能及时收到退出信息sleep(2);kill(pid,SIGKILL);//杀死当前进程exit(0);}}}

以下代码是服务器子进程和父进程的相关代码用于实现对客户端之间的消息转发和客户端进出聊天室的监控。

if((pid = fork())==-1){perror("error to fork.");exit(1);}else if(pid == 0){ //pid=0,当前进程为子进程//消息队列LinkList head;head = CreateLinkList();while(1){//从消息队列中获取一条消息if(msgrcv(msgid,&msg,MSG_LEN,MSG_TO_SERVER,0)==-1){perror("msgrcv error.\n");exit(1);}else{//对接收到的消息,子进程进行消息类型判断switch(msg.subtype){case 1:    //客户端登录if(Insert(head,msg.pid)!=0){printf("insert error.\n");exit(1);}printf("\033[33m%s\033[0m", msg.msg_time);//输出登录时间printf("\033[33m[客户端  %s  登录]\033[0m\n\n",msg.user_name);BroadCast(head,msgid,&msg); //把收到的消息广播break;case 2: //消息广播BroadCast(head,msgid,&msg);if(strncmp(msg.mtext,"quit",4)==0){exit(0);}break;case 3:  //客户端退出if(!Empty(head)){Delete(head,msg.mtype);}printf("\033[33m%s\033[0m", msg.msg_time);//输出退出时间printf("\033[33m[客户端  %s  退出]\033[0m\n\n",msg.user_name);break;default:break;}}}}else{//父进程用于服务器控制台msg.mtype = MSG_TO_SERVER;msg.subtype = 2;while(1){printf("\33[36m*************************************************************\n\33[0m");printf("\33[36m*---------------------服务器控制端--------------------------*\n\33[0m");printf("\33[36m*---------服务端已开启,客户可登陆聊天室进行聊天------------*\n\33[0m");printf("\33[36m*----------------若想关闭服务器,请输入“quit”---------------*\n\33[0m");printf("\33[36m*************************************************************\33[0m\n");fgets(msg.mtext,MSG_SIZE,stdin);//控制台接收到quit时,通知客户端,server结束if(strncmp(msg.mtext,"quit",4)==0){msgsnd(msgid,&msg,MSG_LEN,0);sleep(2);msgctl(msgid,IPC_RMID,NULL);//删除消息队列kill(pid,SIGKILL);  //杀死子进程exit(0);}}}return 0;}LinkList CreateLinkList()
{LinkList head;head = (LinkList)malloc(sizeof(ListNode));head->next = NULL;return head;
}
int Length(LinkList head)
{int n = 0;head = head->next;while(head){n++;head = head->next;}return n;
}
void BroadCast(LinkList head,int msgid,struct msgbuf *msg)
{int i;for(i=0 ;i<Length(head) ;i++){msg->mtype = GetValue(head,i);if(msg->mtype != msg->pid){//mtype = pid说明是发送消息的客户端,不用再接收自己发送的消息msgsnd(msgid,msg,MSG_LEN,0);}}
}int Empty(LinkList head)
{return (head->next == NULL);
}int Insert(LinkList head,int value)
{LinkList p;p = (LinkList)malloc(sizeof(ListNode));p->data = value;p->next = NULL;while(head->next){head = head->next;}head->next = p;return 0;
}void Delete(LinkList head,int value)
{LinkList p = head->next;while(p!=NULL){if(p->data == value){head->next = p->next;free(p);p = head->next;}else{head = p;p = p->next;}}
}int GetValue(LinkList head,int pos)
{head = head->next;while(pos--){head = head->next;}return head->data;
}

Linux实训——单机版聊天室相关推荐

  1. linux实训分析与体会,linux实训总结与体会

    资料简介 证设备之间的正常通信. 了解拓扑结构图是十分重要的课程, 无论什么样的网络 通过总结,人们可以把零散的.肤浅的感性认识上升为系统.深刻的理性认 识,从而得出科学的结论,以便改正缺点,吸取经验 ...

  2. linux实训心得体会范文

    linux实训心得体会范文 当我们有一些感想时,就十分有必须要写一篇心得体会,这样我们可以养成良好的总结方法.相信许多人会觉得心得体会很难写吧,以下是小编精心整理的linux实训心得体会范文,供大家参 ...

  3. linux实训报告内容一万字,Linux实训报告.doc

    Linux实训报告.doc 湖南娄底职业技术学院 LinuxLinux 网络服务器配置与管理网络服务器配置与管理 实训报告实训报告 设计题目 Linux 基础及应用实训 系 部 电子信息工程系 专 业 ...

  4. linux服务器实训心得体会,linux实训心得体会

    linux实训心得体会 我们得到了一些心得体会以后,写心得体会是一个不错的选择,这么做可以让我们不断思考不断进步.那么好的心得体会都具备一些什么特点呢?以下是小编帮大家整理的linux实训心得体会,仅 ...

  5. linux系统实训总结报告,linux实训心得体会范文

    linux实训心得体会范文 当我们有一些感想时,就十分有必须要写一篇心得体会,这样我们可以养成良好的总结方法.相信许多人会觉得心得体会很难写吧,以下是小编精心整理的linux实训心得体会范文,供大家参 ...

  6. linux实训报告内容摘要,linux实训报告心得

    linux实训报告心得 时间:2017/12/2 18:43:00 linux实训报告心得[1] 我们这一代90后,从小接触的是windows98,家里条件好的自己有电脑装的是2010,后来又有了xp ...

  7. linux服务器实训心得体会,linux实训心得体会 linux实训总结与体会

    <linux实训心得体会 linux实训总结与体会>由会员分享,可在线阅读,更多相关<linux实训心得体会 linux实训总结与体会(7页珍藏版)>请在人人文库网上搜索. 1 ...

  8. linux下的网络聊天室

    linux下的网络聊天室 一版:多reactor模式实现高并发服务器. sever.c #include "msg.h" #include "pthreadpool.h& ...

  9. linux实训项目——飞鸽(一)

    目前完成UDP广播上线.用户间对话功能. 目录 目录 前言 一.基础知识 二."飞鸽"运行流程 1.用户运行程序后先设置用户名,即上线后别人看到的名字. 2.上线后进行UDP广播, ...

最新文章

  1. html 列表bootstrap,bootstrap都有哪些类?
  2. BZOJ4373: 算术天才⑨与等差数列
  3. 手动编译Java Web 的Hibernate的工程
  4. 数据挖掘实战(三):特征工程-二手车交易价格预测
  5. 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案
  6. ubuntu命令行启动浏览器_Ubuntu 秘笈之命令行下管理浏览器书签
  7. 计算机网络第七版总结报告,计算机网络(第七版)第一章总结(超详细!)
  8. python 图像处理_Python常用库-Pillow图像处理
  9. 基于JAVA+SpringMVC+Mybatis+MYSQL的网上医院预约挂号系统
  10. bat调用ant_bat调用ant_Ant调用批处理文件编译VS2005程序
  11. Queue--队列(c语言简单实现)
  12. 收集整理的125个微信小程序模板源码
  13. 2021-01-31
  14. 新浪通行证在线申诉找回密码业务逻辑错误导致严重安全漏洞
  15. 安徽大学836数字电路与逻辑设计考研历年真题库资料
  16. Win11 鼠标右键后怎么设置让其显示更多可操作的选项
  17. STC89C51系列 EEPROM测试程序 证明扇区512个字节的擦除
  18. Navicat Premium 导入SQL文件
  19. Tableau 网站流量分析案例汇总
  20. 重磅!京东云自研第四代云主机发布;曝国外物理学家开发出用于量子计算机的汇编语言...

热门文章

  1. 计算机改变了我们的生活英语作文带翻译,关于未来生活的高中英语作文带翻译...
  2. AI安防智能化发展至今还存在哪些问题?
  3. kotlin when (while)循环
  4. Ubuntu系统中Qt运行提示报错:Error while building/deploying project untitled (kit: 桌面) When executing s
  5. 什么是缓冲区溢出?有说明危害?
  6. mysql boolean类型_mysql 布尔类型
  7. 操作系统和应用的关系
  8. java param request_java-将@RequestParam作为列表是不可能的吗?
  9. forever 使用
  10. 现代软件与游戏的创新性分析