基于Linux的消息队列及多线程编程实现的聊天室(一)

本程序主要是针对Linux IPC通信初学者对Linux下消息队列通信机制,多线程编程,字符串处理,链表操作,信号简单处理等基本概念的练习。

原理:

  消息队列是System V支持一种IPC机制,通过类似链表的操作向一个FIFO里通过msgsnd发送用户自定义数据,进程可以通过msgrcv来接收指定类似mtype的数据,从而实现进程间通信。

主要实现了以下功能:

  1. > 通过多个终端登录,不同终端上登录用户实现私聊
  2. > 群聊
  3. > 查看在线用户
  4. > 简单注册(没有实现用户保存,类似于公共聊天室)

下面是几种操作的处理流程分析。详细代码分析见下篇博文:

          http://blog.csdn.net/mr_raptor/article/details/8484822

代码下载:

          http://download.csdn.net/detail/mr_raptor/4976808


 >> 服务器通过特定的类型mtype:1000,从消息队列上接收数据。

 >> 当前登录用户将随机产生(random())的一个随机ID号作为申请用户ID,使用如下协议向服务器(mtype=1000)发送申请消息。

@msg.h

协议数据定义如下,共有4个段,以“:”作为分隔符。

[cpp]  view plain copy
  1. // CMD:FROM:TIME:DATA   
  2. #define     DATA_LEN    4  
  3. #define     OFT_CMD     0  
  4. #define     OFT_FRM     1  
  5. #define     OFT_TIM     2  
  6. #define     OFT_DAT     3  
  7. #define     DATA_TOK    ":"  

CMD:FROM:TIME:DATA

  CMD:表示执行的操作

  FROM:表示来自哪个终端

  TIME:申请时间

  DATA:用户发送数据

  >> 服务器在接收到用户申请请求后,从可用ID里取出可用ID号(可用ID从START_ID开始向上累加)配给新申请用户,将其加入到服务器维护的链表里,然后将新分配ID号写回到客户端(使用客户端随机产生的ID号写回),并且以后通信都通过新的ID号作用消息队列的mtype。


@msg.h

[cpp]  view plain copy
  1. #define     START_ID    1  
  >> 登录用户接收到服务器分配的新ID后,开启接收消息线程等待接收来自消息队列里发送给自己的消息。

@msg_client.c

[cpp]  view plain copy
  1. if(login() == OK)  
  2.     while(pthread_create(&thread, NULL, receiver_looper, NULL) < 0);  
  3.    break;  

[cpp]  view plain copy
  1. void * receiver_looper(void * p){  
  2.     if(userid == 0)  
  3.         return NULL;  
  4.     char * data[DATA_LEN];  
  5.     char * str, *subtoken;  
  6.     int i;  
  7.     while(1){  
  8.         if(msgrcv(msgid, &msg_rcv, sizeof(msg_rcv), userid, 0) < 0){  
  9.             perror("msgrcv");  
  10.             continue;  
  11.         }else{  


  >> 当用户在消息队列上收到消息后,按照前面说的通信协议解析(strtok())接收到的数据,格式化后依据不同的CMD操作用于分支处理。
[cpp]  view plain copy
  1. #ifdef _DEBUG  
  2.             printf("%s received: %s\n", __func__, msg_rcv.buffer);  
  3. #endif  
  4.             memset(data, NULL, sizeof(data));  
  5.             for(str = msg_rcv.buffer, i = 0; ; str = NULL, i++){  
  6.                 subtoken = strtok(str, DATA_TOK);  
  7.                 if(subtoken == NULL)  
  8.                     break;  
  9.                 data[i] = subtoken;  
  10. #ifdef _DEBUG  
  11.                 printf("> data[%d] = %s\n", i, subtoken);  
  12. #endif  
  13.             }  
  14.             // process received data  
  15.             // data format error  
  16.             if(i != DATA_LEN)  
  17.                 continue;  
  18.   
  19.             switch(data[OFT_CMD][0]){  
  20.             case CMD_LIST:  
  21.                 if(strcmp(data[OFT_FRM], TYPE_SERVER_STR)){  
  22.                     continue;  
  23.                 }  
  24.                 format_user_list(data[OFT_DAT]);  
  25.                 break;  
  26.             case CMD_LOGOUT:  
  27.                 if(strcmp(data[OFT_FRM], TYPE_SERVER_STR)){  
  28.                     continue;  
  29.                 }  
  30.                 printf("> %s ", data[OFT_DAT]);  
  31.                 printf("%s\n", time2str(atol(data[OFT_TIM]), data[OFT_DAT]));  
  32.                 exit(0);  
  33.             case CMD_CHAT:      // print chat content  
  34.                 printf("\n%s \n\t\t", data[OFT_DAT]);  
  35.                 printf("%s\n", time2str(atol(data[OFT_TIM]), data[OFT_DAT]));  
  36.                 printf("\n%s# ", name);  
  37.                 fflush(stdout);  
  38.                 break;                                                          
  39.             case CMD_SEND_FILE:  
  40.                 break;  
  41.             }  
  42.         }  

  >> 群聊消息发送给服务器,服务器收到后,遍历在线链表,向每个在线用户发送消息。

@msg_svr.c

下面是分支处理代码片段:

[cpp]  view plain copy
  1. case CMD_TOALL:  
  2.     // send to all online client  
  3.     p = (&msg_list_head)->next;  
  4.     while(p){  
  5.         u= (struct user*)p;  
  6.         send_msg(u->id, CMD_CHAT, data[OFT_FRM], data[OFT_DAT]);  
  7.         p = p->next;  
  8.     }  
  9.     break;  

注:本程序只能运行在一个主机上不同终端之间,不能实现跨主机通信。


运行情况如下:

服务器运行情况:

主要打印客户端的用户操作,消息转发等信息。


客户端登录:初始帮助信息



用户登录及列出在线用户:


另外一个终端登录luccy用户,列出在线用户:


私聊:


另外一个终端收到信息:


群聊,两个终端都收到信息:




退出:



另外还有文件传输功能,留给同学们自己去实现吧。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
项目简介: 采用I/O复用技术select实现socket通信,采用多线程负责每个客户操作处理,完成Linux下的多客户聊天室! OS:Ubuntu 15.04 IDE:vim gcc make DB:Sqlite 3 Time:2015-12-09 ~ 2012-12-21 项目功能架构: 1. 采用client/server结构; 2. 给出客户操作主界面(注册、登录、帮助和退出)、登录后主界面(查看在线列表、私聊、群聊、查看聊天记录、退出); 3. 多客户可同时连接服务器进行自己操作; ##服务器端## 1. server.c:服务器端主程序代码文件; 2. config.h:服务器端配置文件(包含需要的头文件、常量、数据结构及函数声明); 3. config.c:服务器端公共函数的实现文件; 4. list.c:链表实现文件,用于维护在线用户链表的添加、更新、删除操作; 5. register.c:服务器端实现用户注册; 6. login.c:服务器端实现用户登录; 7. chat.c:服务器端实现用户的聊天互动操作; 8. Makefile:服务器端make文件,控制台执行make命令可直接生成可执行文件server ##客户端## 1. client.c:客户端主程序代码文件; 2. config.h:客户端配置文件(包含需要的头文件、常量、数据结构及函数声明); 3. config.c:客户端公共函数的实现文件; 4. register.c:客户端实现用户注册; 5. login.c:客户端实现用户登录; 6. chat.c:客户端实现用户的聊天互动操作; 7. Makefile:客户端make文件,控制台执行make命令可直接生成可执行文件client;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值