实现功能:利用程序生成4个可执行程序,A、B、C、D 实现他们之间互发消息和群发消息。
用到的内容:线程,消息队列
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
int msgget(key_t key, int msgflg);
需要的头文件
#include
#include
#include
功能:利用key创建消息队列,如果没有此消息队列就创建,如果有就返回文件描述符
msqid = msgget(key, IPC_CREAT|0777)
其他更详细功能参数上面那篇博客。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
需要的头文件
#include
#include
#include
功能: 给相应的消息队列发送数据
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字节,则把该消息截断,截断部分将被丢弃
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
功能:创建线程函数
后面会整理一篇参数详细解释,这里会用就好了
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;
}
修改程序中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选项。
下面A给B发消息hello,I am A.
D 给A发 Nice to meet you!
测试结果
下面测试群发
C给所有人发 Good morning, everybody.
测试成功!
以上就是我用消息队列做的一个小dome,实现进程间的通讯,群发和单发的一个效果,由于是新手代码方面可能有所纰漏欢迎大家指出与交流。
有什么不理解的地方欢迎大家和我讨论!!!!