Linux系统下进程间利用消息队列实现广播和点对点通讯(C语言)

进程间消息队列实现广播和点对点通讯

实现功能:利用程序生成4个可执行程序,A、B、C、D 实现他们之间互发消息和群发消息。

用到的内容:线程,消息队列

Linux系统下进程间利用消息队列实现广播和点对点通讯(C语言)_第1张图片
程序实现流程图

用到的主要函数及其说明

1.ftok函数

key_t ftok(const char *pathname, int proj_id);
需要的头文件
#include
#include
主要功能:利用一个存在的路径和一个有意义的八位的proj_id(不能为0)返回一个key_t类型的值,在后面创建消息队列时候会用到这个值。
这里我的路径是“.”,proj_id是‘z’.
值得说明的一点:如果多次执行这个函数。在路径和proj_id一样的情况下返回的 key_t类型的值是一样的,所以四个执行程序都可以访问到同一个消息队列
这里只做简单介绍,想了解更多就去终端 man ftok就好了

下面函数的功能介绍这篇大佬的博客里整理的很清楚,直接去看一下怎么用就好了,我这里就不赘述了。
https://blog.csdn.net/guoping16/article/details/6584024

2.mssget函数

int msgget(key_t key, int msgflg);
需要的头文件
#include
#include
#include
功能:利用key创建消息队列,如果没有此消息队列就创建,如果有就返回文件描述符
msqid = msgget(key, IPC_CREAT|0777)
其他更详细功能参数上面那篇博客。

3. msgsnd函数

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
需要的头文件
#include
#include
#include
功能: 给相应的消息队列发送数据

4. msgrcv函数

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
需要的头文件
同msgsnd;
功能:获取队列中的消息。
msqid:消息队列描述符
msgsz:到接受数据的大小,返回值就是这个大小,msgtyp指定要接收消息类型。不同的情况在这个博客里都有。这里也简单写一下。
msgtyp
0:接收第一个消息
大于0:接收类型等于msgtyp的第一个消息
小于0:接收类型等于或者小于msgtyp绝对值的第一个消息
msgflg
0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待
IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG
MSG_COPY:与msgtype配合使用返回队列中第一个类型为msgtype的消息,不能与MSG_EXCEPT一起用。
MSG_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息
MSG_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃

5. phread_create函数

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
功能:创建线程函数
后面会整理一篇参数详细解释,这里会用就好了

6.pread_join函数

int pthread_join(pthread_t thread, void **retval);
功能: 和phread_create()函数配套使用。函数会一直阻塞调用线程,直到指定的线程终止。当pthread_join()返回之后,应用程序可回收与已终止线程关联的任何数据存储空间。

进入正题,代码实现

#include<stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/msg.h>

// 用于创建一个唯一的key
#define MSG_FILE "."
//生成不同的程序需要替换MY_NAME定义A-D
#define MY_NAME 'D'
//程序执行个数
#define NUM_DEV 4

// 消息结构
typedef struct msg_form {
    long mtype;
    char mtext[256];
	int  mfrom;
}MSG_FROM;

//SENDTOA,RECVFROMA,SENDTOB,RECVFROMB,SENDTOC,RECVFROMC,SENDTOD,RECVFROMD
//八个数分别为A接收的消息类型,A消息来源号 依次类推BCD
const int msgdata[8] = {111,112,222,223,333,334,444,445};
MSG_FROM msg; 
long msqid;
key_t key;

int msgfrom_value()
{
	int i = 0;
	i = MY_NAME - 'A';
	i = 2*i+1;
	return msgdata[i];
}

void *recvmsg()
{
	int ret = 0;
	long type = msgfrom_value()-1;
	while(1)
	{
		ret = msgrcv(msqid, &msg, 256+sizeof(int),
				 type, IPC_NOWAIT|MSG_INFO);// 返回类型为888的第一个消息
		while(ret != -1)
		{
			printf("%s\n", msg.mtext);
			//printf("%ld\n", msg.mtype);
			if(msg.mfrom != 0)
			{
				memset(&msg.mtext,'\0',256);
				msg.mtype = msg.mfrom-1; // 客户端接收的消息类型
				msg.mfrom = 0;
				sprintf(msg.mtext, "%c: I get it! %d\n", MY_NAME, getpid());
				msgsnd(msqid, &msg, sizeof(msg.mtext)+sizeof(int), 0);
			}
			sleep(1);
			ret = msgrcv(msqid, &msg, 256+sizeof(int),
				 type, IPC_NOWAIT|MSG_INFO);// 返回类型为888的第一个消息
		}
	}
}

void *sendmsg()
{
	char s[256];
	char ch;
	int all = -1;
	while(1)
	{
		while(all == -1)
		{
		    memset(&msg.mtext,'\0',256);
			printf("please input the equitment you want to send.,,\n");
			scanf("%c",&ch);
			getchar();
			switch(ch)
			{
				case 'A':{
						msg.mtype = msgdata[0];
						all = 0;
					}
					break;
				case 'B':{
						msg.mtype = msgdata[2];
						all = 0;
					}
					break;
				case 'C':{
						msg.mtype = msgdata[4];
						all = 0;
					}
					break;
				case 'D':{
						msg.mtype = msgdata[6];
						all = 0;
					}
				break;
				case 'O':{
						all = 1;
					}
				break;
				default:{
						all = -1;
						printf("please input right name again...\n");
					}
				break;
			}
		}
		if(all == 0)
		{
			memset(s, '\0', sizeof(char)*256);
			printf("please input your message...\n");
			scanf("%[^\n]",s);
			getchar();
			sprintf(msg.mtext, "%c: %s", MY_NAME, s);
			msg.mfrom = msgfrom_value();
			msgsnd(msqid, &msg, sizeof(msg.mtext)+sizeof(int), 0);
		}
		else if(all == 1)
		{
			memset(s, '\0', sizeof(char)*256);
			printf("please input your message...\n");
			scanf("%[^\n]",s);
			getchar();
			sprintf(msg.mtext, "%c: %s", MY_NAME, s);
			msg.mfrom = msgfrom_value();
			msg.mtype = 0;
			int i = 0;
			for(i = 0; i < NUM_DEV; i++)
			{
				msg.mtype = msgdata[2*i];
				msgsnd(msqid, &msg, sizeof(msg.mtext)+sizeof(int), 0);
			}
		}
		sleep(1);
		all = -1;
	}
}

int main()
{
	printf("I am %c!\n",MY_NAME);
    // 获取key值
    if((key = ftok(MSG_FILE,'z')) < 0)
    {
        perror("ftok error");
        exit(1);
    }

    // 打印key值
    printf("Message Queue - Server key is: %d.\n", key);

    // 创建消息队列
    if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
    {
        perror("msgget error");
        exit(1);
    }

    // 打印消息队列ID及进程ID
    printf("My msqid is: %ld.\n", msqid);
    printf("My pid is: %d.\n", getpid());

	//pthread_mutex_init(&mute, NULL);
	//创建子线程1
	pthread_t tid1;//定义子线程标识符
    pthread_create(&tid1,NULL,recvmsg,0);//pthread_1即线程执行函数

	//创建子线程2
    pthread_t tid2;//定义子线程标识符
    pthread_create(&tid2,NULL,sendmsg,0);//pthread_2即线程执行函数

	pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    printf("child pthread exit\n");

    //sleep(5);
    printf("main pthread exit\n");
    pthread_exit(NULL);  //结束主线程 
    return 0;
}

实现方式(图解)

Linux系统下进程间利用消息队列实现广播和点对点通讯(C语言)_第2张图片上为程序实现原理图解

编译

修改程序中MY_NAME 为 A B C D 分别编译
编译指令:gcc 文件名 -o A -pthread
如我的文件名为 A_msg_server_client.c
我的编译指令为 gcc A_msg_server_client.c -o A -pthread
如果要增加数量,可以修改程序中 msgdata[8]#define NUM_DEV 4以及添加 *sendmsg()函数中的case选项。

运行结果

Linux系统下进程间利用消息队列实现广播和点对点通讯(C语言)_第3张图片
在终端运行编译好的程序
Linux系统下进程间利用消息队列实现广播和点对点通讯(C语言)_第4张图片程序运行后打印的信息

下面A给B发消息hello,I am A.
D 给A发 Nice to meet you!
测试结果
Linux系统下进程间利用消息队列实现广播和点对点通讯(C语言)_第5张图片下面测试群发
C给所有人发 Good morning, everybody.
Linux系统下进程间利用消息队列实现广播和点对点通讯(C语言)_第6张图片测试成功!

总结

以上就是我用消息队列做的一个小dome,实现进程间的通讯,群发和单发的一个效果,由于是新手代码方面可能有所纰漏欢迎大家指出与交流。

有什么不理解的地方欢迎大家和我讨论!!!!

你可能感兴趣的:(Linux系统下进程间利用消息队列实现广播和点对点通讯(C语言))