用 System V IPC 消息队列实现简单的 Client&Server 通信

一、具体实现

$ ls
client  client.c  common.c  common.h  Makefile  server  server.c
$ cat Makefile
.PHONY:all
all: client server

client:client.c common.c
  gcc $^ -o $@

server:server.c common.c
  gcc $^ -o $@

.PHONY:clean
clean:
  rm client server

comm.h

#pragma once

#define FILEPATH "."
#define PROJ_ID  1

typedef struct Msgbuf
{
  long type;
#define MSG_TEXT_LEN 1024
  char text[MSG_TEXT_LEN];
} Msgbuf;

enum Msgtype
{
  /*
   * server 端要做的事情
   * 1、从消息队列中读取数据
   * 2、根据读到的数据进行计算,生成响应数据
   * 3、把响应数据写回到消息队列
   *
   * client 端要做的事情
   * 1、从标准输入读入数据
   * 2、把数据写到消息队列中
   * 3、从消息队列中读取数据
   * 4、把结果打印到显示器上
   *
   * 当前业务场景下涉及到的消息类型就只有客户端请求和服务端响应这两种
   * 注意,Msgbuf.type 必须大于 0,所以才将初始值设置成 1
   */
  Request = 1,
  Response
};

int CreateMsg();
int OpenMsg();
int GetMsgqbytes(int msgid);
int DestroyMsg(int msgid);
int SendMsg(int msgid, char *buf, int size, int type);
int SendMsg2(int msgid, char *buf, int size, int type);
int RecvMsg(int msgid, char *buf, int size, int type);

comm.c

#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include "common.h"

int CreateMsg()
{
  key_t key;
  int   msgid;

  key = ftok(FILEPATH, PROJ_ID);
  if (key < 0)
  {
    perror("ftok");
    return -1;
  }

  msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
  if (msgid < 0)
  {
    perror("msgget");
    return -1;
  }

  return msgid;
}

int OpenMsg()
{
  key_t key;
  int   msgid;

  key = ftok(FILEPATH, PROJ_ID);
  if (key < 0)
  {
    perror("ftok");
    return -1;
  }

  msgid = msgget(key, IPC_CREAT);
  if (msgid < 0)
  {
    perror("msgget");
    return -1;
  }

  return msgid;
}

int GetMsgqbytes(int msgid)
{
  struct msqid_ds buf;

  memset(&buf, sizeof(struct msqid_ds), 0x00);
  if (msgctl(msgid, IPC_STAT, &buf) < 0)
  {
    perror("msgctl");
    return -1;
  }

  return buf.msg_qbytes;
}

int DestroyMsg(int msgid)
{
  if (msgctl(msgid, IPC_RMID, NULL) < 0)
  {
    perror("msgctl");
    return -1;
  }

  return 0;
}

int SendMsg(int msgid, char *buf, int size, int type)
{
  Msgbuf mbuf;
  int    ret;

  if (size >= (int)MSG_TEXT_LEN)
  {
    printf("缓冲区长度不够\n");
    return -1;
  }

  mbuf.type = type;
  strcpy(mbuf.text, buf);
  mbuf.text[size] = '\0';
  ret = msgsnd(msgid, &mbuf, MSG_TEXT_LEN, 0); /* 阻塞 */
  if (ret < 0)
  {
    perror("msgsnd");
    return -1;
  }

  return 0;
}

int SendMsg2(int msgid, char *buf, int size, int type)
{
  Msgbuf mbuf;
  int    ret;

  if (size >= (int)MSG_TEXT_LEN)
  {
    printf("缓冲区长度不够\n");
    return -1;
  }

  mbuf.type = type;
  strcpy(mbuf.text, buf);
  mbuf.text[size] = '\0';
  ret = msgsnd(msgid, &mbuf, MSG_TEXT_LEN, IPC_NOWAIT); /* 非阻塞 */
  if (ret < 0)
  {
    perror("msgsnd");
    return -1;
  }

  return 0;
}

int RecvMsg(int msgid, char *buf, int size, int type)
{
  /*(void)size;*/
  Msgbuf  mbuf;
  ssize_t ret;

  if (size < MSG_TEXT_LEN)
  {
    printf("缓冲区长度不够\n");
    return -1;
  }

  memset(mbuf.text, MSG_TEXT_LEN, 0x00);
  ret = msgrcv(msgid, &mbuf, MSG_TEXT_LEN, type, 0); /* 阻塞 */
  if (ret < 0)
  {
    perror("msgrcv");
    return -1;
  }

  strcpy(buf, mbuf.text);
  return 0;
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include "common.h"

int main()
{
  int  msgid;
  char buf[1024];

  msgid = OpenMsg();
  if (msgid < 0)
    return EXIT_FAILURE;

  while(1)
  {
    ssize_t s;

    while (1)
    {
      printf("please enter#");
      fflush(stdout);
      s = read(0, buf, sizeof(buf) - 1);
      if (s > 0)
        break;
    }
    SendMsg(msgid, buf, s, Request);

    RecvMsg(msgid, buf, sizeof(buf), Response);
    printf("server#%s", buf);
    //fflush(stdout);
  }
  
  return 0;
}

service.c

#include <stdio.h>
#include <stdlib.h>
#include "common.h"

int main()
{
  int  msgid;
  char buf[MSG_TEXT_LEN];

  msgid = CreateMsg();
  if (msgid < 0)
    return EXIT_FAILURE;

  while(1)
  {
    ssize_t s;

    RecvMsg(msgid, buf, sizeof(buf), Request);
    printf("client#%s", buf);
    //fflush(stdout);

    while (1)
    {
      printf("please enter#");
      fflush(stdout);
      s = read(0, buf, sizeof(buf) - 1);
      if (s > 0)
        break;
    };

    SendMsg(msgid, buf, s, Response);
  }

  DestroyMsg(msgid);
  return 0;
}

在这里插入图片描述

二、验证 MSGMNB 默认值

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "common.h"

int main()
{
  int  msgid, i;
  char buf[1024] = {0};

  msgid = CreateMsg();
  if (msgid < 0)
    return EXIT_FAILURE;

  i = GetMsgqbytes(msgid);
  if (i < 0)
    return EXIT_FAILURE;
  else
    printf("msg_qbytes: %d\n", i);

  strcpy(buf, "test data");
  i = 0;
  while (0 == SendMsg2(msgid, buf, strlen(buf), Request))
    i++;

  printf("message num: %d\n", i);
  /* 暂不销毁该消息队列,利用 ipcs -q 加以验证测试结果 */
  //DestroyMsg(msgid);  
  return 0;
}
$ ./test_msgmnb
msg_qbytes: 16384
msgsnd: Resource temporarily unavailable
message num: 16
$ ipcs -q

------ Message Queues --------
key        msqid     owner      perms      used-bytes   messages
0x00000000 0         root       660        0            0
0x00000000 1         root       660        0            0
0x00000000 2         root       660        0            0
0x01026636 21        mam        666        16384        16

$
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值