基于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个段,以“:”作为分隔符。

// CMD:FROM:TIME:DATA 
#define     DATA_LEN    4
#define     OFT_CMD     0
#define     OFT_FRM     1
#define     OFT_TIM     2
#define     OFT_DAT     3
#define     DATA_TOK    ":"

CMD:FROM:TIME:DATA

  CMD:表示执行的操作

  FROM:表示来自哪个终端

  TIME:申请时间

  DATA:用户发送数据

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


@msg.h

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

@msg_client.c

 if(login() == OK)
     while(pthread_create(&thread, NULL, receiver_looper, NULL) < 0);
    break;

void * receiver_looper(void * p){
    if(userid == 0)
        return NULL;
    char * data[DATA_LEN];
    char * str, *subtoken;
    int i;
    while(1){
        if(msgrcv(msgid, &msg_rcv, sizeof(msg_rcv), userid, 0) < 0){
            perror("msgrcv");
            continue;
        }else{


  >> 当用户在消息队列上收到消息后,按照前面说的通信协议解析(strtok())接收到的数据,格式化后依据不同的CMD操作用于分支处理。
#ifdef _DEBUG
            printf("%s received: %s\n", __func__, msg_rcv.buffer);
#endif
            memset(data, NULL, sizeof(data));
            for(str = msg_rcv.buffer, i = 0; ; str = NULL, i++){
                subtoken = strtok(str, DATA_TOK);
                if(subtoken == NULL)
                    break;
                data[i] = subtoken;
#ifdef _DEBUG
                printf("> data[%d] = %s\n", i, subtoken);
#endif
            }
            // process received data
            // data format error
            if(i != DATA_LEN)
                continue;

            switch(data[OFT_CMD][0]){
            case CMD_LIST:
                if(strcmp(data[OFT_FRM], TYPE_SERVER_STR)){
                    continue;
                }
                format_user_list(data[OFT_DAT]);
                break;
            case CMD_LOGOUT:
                if(strcmp(data[OFT_FRM], TYPE_SERVER_STR)){
                    continue;
                }
                printf("> %s ", data[OFT_DAT]);
                printf("%s\n", time2str(atol(data[OFT_TIM]), data[OFT_DAT]));
                exit(0);
            case CMD_CHAT:      // print chat content
                printf("\n%s \n\t\t", data[OFT_DAT]);
                printf("%s\n", time2str(atol(data[OFT_TIM]), data[OFT_DAT]));
                printf("\n%s# ", name);
                fflush(stdout);
                break;                                                        
            case CMD_SEND_FILE:
                break;
            }
        }

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

@msg_svr.c

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

 case CMD_TOALL:
     // send to all online client
     p = (&msg_list_head)->next;
     while(p){
         u= (struct user*)p;
         send_msg(u->id, CMD_CHAT, data[OFT_FRM], data[OFT_DAT]);
         p = p->next;
     }
     break;

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


运行情况如下:

服务器运行情况:
基于Linux的消息队列及多线程编程实现的聊天室(一)_第1张图片
主要打印客户端的用户操作,消息转发等信息。


客户端登录:初始帮助信息
基于Linux的消息队列及多线程编程实现的聊天室(一)_第2张图片


用户登录及列出在线用户:
基于Linux的消息队列及多线程编程实现的聊天室(一)_第3张图片

另外一个终端登录luccy用户,列出在线用户:
基于Linux的消息队列及多线程编程实现的聊天室(一)_第4张图片

私聊:


另外一个终端收到信息:
基于Linux的消息队列及多线程编程实现的聊天室(一)_第5张图片

群聊,两个终端都收到信息:
基于Linux的消息队列及多线程编程实现的聊天室(一)_第6张图片

基于Linux的消息队列及多线程编程实现的聊天室(一)_第7张图片

退出:



另外还有文件传输功能,留给同学们自己去实现吧。






你可能感兴趣的:(基于Linux的消息队列及多线程编程实现的聊天室(一))