本程序主要是针对Linux IPC通信初学者对Linux下消息队列通信机制,多线程编程,字符串处理,链表操作等基本概念的练习。
原理:
消息队列是System V支持一种IPC机制,通过类似链表的操作向一个FIFO里通过msgsnd发送用户自定义数据,进程可以通过msgrcv来接收指定类似mtype的数据,从而实现进程间通信。
主要实现了以下功能:
1.通过多个终端登录,不同终端上登录用户实现私聊
2.群聊
3.查看在线用户
4.简单注册(没有实现用户保存,类似于公共聊天室)
脉冲宽度调制(PWM)功能是使用TCMPBn寄存器的值来实现的。在定时器控制逻辑中,如果递减计数器的值 匹配 比较寄存器的值,定时器的控制逻辑将更改输出的状态。因此,比较寄存器决定了一个PWM输出开始时间(或关闭时间)。
下面是几种操作的处理流程分析。详细代码分析见下篇博文:
//blog.csdn.net/mr_raptor/article/details/8484822
代码下载:
//download.csdn.net/detail/mr_raptor/4976808
>> 服务器通过特定的类型mtype:1000,从消息队列上接收数据。
>> 当前登录用户将随机产生(random())的一个ID号作为申请,如下协议向服务器1000发送申请消息。
@msg.h
[cpp] view plaincopyprint?
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号作用消息队列的mtype。
@msg.h
[cpp] view plaincopyprint?
1.#define START_ID 1
>> 登录用户接收到服务器分配的新ID后,开启接收消息线程等待接收来自消息队列里,并且发送能自己的消息。
@msg_client.c
[cpp] view plaincopyprint?
1.login();
2.while(pthread_create(&thread, NULL, receiver_looper, NULL) < 0);
3.break;
[cpp] view plaincopyprint?
1.void * receiver_looper(void * p){
2. if(userid == 0)
3. return NULL;
4. char * data[DATA_LEN];
5. har * 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 plaincopyprint?
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 plaincopyprint?
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用户,列出在线用户:
私聊:
另外一个终端收到信息:
群聊,两个终端都收到信息:
退出:
另外还有文件传输功能,留给同学们自己去实现吧。