php与linux程序的消息队列通信

概述

最近正在设计一些关于PHP与linux程序进行通信的工作,首先php和linux程序一般是不会运行在一个进程里,这样如果需要通信只能是跨进程实现。

这里可以使用两种方法,一种是用c语言分别实现通信接口,然后将其中一种编译成php扩展,让php调用,这类自由度比较高,但是有一定难度。还有一种就是直接使用php支持的ipc接口,与其他进程通信。

今天我们来研究一下php使用现有的IPC与其他进程通信的方法,这个方法在大多数时候都是足够满足需求的,下面我们来分几个步骤一一实现这个功能。

linux消息队列例程

发送例程

send.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct{
    long type;
    char name[20];
}Msg;

int main()
{
    key_t key = ftok("/home",'6');
    printf("key:%x\n",key);

    int msgid = msgget(key,IPC_CREAT|O_WRONLY|0777);
    if(msgid<0)
    {   
        perror("msgget error!");
        exit(-1);
    }   

    Msg m;
    m.type = 6;
    memcpy(m.name, "hello!", 7);
    msgsnd(msgid,&m,sizeof(m)-sizeof(m.type),0);

    return 0;
}

非常简单,就是将一个20个字节大小的数据通过消息队列发送出去。

接收例程

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct{
    long type;
    char name[20];
}Msg;

int main()
{
    key_t key = ftok("/home",'6');
    printf("key:%x\n",key);

    int msgid = msgget(key,O_RDONLY|IPC_CREAT|0777);
    if(msgid<0)
    {   
        perror("msgget error!");
        exit(-1);
    }   

    Msg rcv;
    long type = 6;

    msgrcv(msgid,&rcv,sizeof(rcv)-sizeof(type),type,0);
    printf("rcv--name:%s\n",rcv.name);

    msgctl(msgid,IPC_RMID,NULL);
    return 0;
}

和发送例程对应,接收数据并打印出来。

运行结果如下:

~/test/msg$ ./recv
key:36020001
rcv--name:hello!

PHP发送例程

先上代码send.php

<?php 
$message_queue_key = ftok("/home",'6'); 
$message_queue = msg_get_queue($message_queue_key, 0777); 

//向消息队列中写 
msg_send($message_queue, 6, "Hello!", FALSE); 
?> 

非常简单,对于主要函数可以和c语言的一一对应,具体参数可以查看如下网站介绍

https://www.php.net/manual/en/function.ftok.php

fork对应fork,msg_get_queue对应msgget, msg_send对应msgsnd。

运行结果如下:

$ php ./send.php

$ ./recv
key:36020001
rcv--name:Hello!

PHP接收例程

<?php 
$message_queue_key = ftok("/home",'6'); 
$message_queue = msg_get_queue($message_queue_key, 0777); 

//从消息队列中读 
msg_receive($message_queue, 6, $message_type, 1024, $message, FALSE); 
print_r("php rcv--name:".$message."\n"); 
msg_remove_queue($message_queue); 
?> 

同样很简单,也可以与c语言的对应上。

fork对应fork,msg_get_queue对应msgget, msg_receive对应msgrcv,msg_remove_queue对应msgctl(msgid,IPC_RMID,NULL);删除消息队列。

运行结果如下:

$ ./send
key:36020001

$ php ./recv.php 
php rcv--name:hello!

到此我们已经可以使用php与c语言的程序进行消息队列通信,但是有一点不足就是消息之传输了字符串。

结构体的支持

在c语言中消息往往是复合类型的结构体,那么我们如何与php传输结构体呢?如我们所知,php是弱类型变量,没有结构体概念年,类似的有数组和类,但是这个结构不能与c语言的结构体一一对应,好在php提供了pack和unpack函数。

pack — 将数据打包成二进制字符串

参考网站:https://www.php.net/manual/zh/function.pack.php

unpack — 从字串中的二进制串转化成指定的格式

目前已实现的格式如下:

pack() 格式字符
代码描述
a以NUL字节填充字符串
A以SPACE(空格)填充字符串
h十六进制字符串,低位在前
H十六进制字符串,高位在前
c有符号字符
C无符号字符
s有符号短整型(16位,主机字节序)
S无符号短整型(16位,主机字节序)
n无符号短整型(16位,大端字节序)
v无符号短整型(16位,小端字节序)
i有符号整型(机器相关大小字节序)
I无符号整型(机器相关大小字节序)
l有符号长整型(32位,主机字节序)
L无符号长整型(32位,主机字节序)
N无符号长整型(32位,大端字节序)
V无符号长整型(32位,小端字节序)
q有符号长长整型(64位,主机字节序)
Q无符号长长整型(64位,主机字节序)
J无符号长长整型(64位,大端字节序)
P无符号长长整型(64位,小端字节序)
f单精度浮点型(机器相关大小)
g单精度浮点型(机器相关大小,小端字节序)
G单精度浮点型(机器相关大小,大端字节序)
d双精度浮点型(机器相关大小)
e双精度浮点型(机器相关大小,小端字节序)
E双精度浮点型(机器相关大小,大端字节序)
xNUL字节
X回退已字节
Z以NUL字节填充字符串空白(PHP 5.5中新加入的)
@NUL填充到绝对位置

例如:

typedef struct{
    char name[20];
    int age;
}Msg;

下面看一下PHP如何将他们转换在转回来的

//将PHP变量转成结构
$name = "13901234567";
$age = 33;
$returnstr = sprintf("%-20s",$name).pack("l",$age);

//将结构转成php变量
//对于字符型变量可以直接取得
$name = substr($returnstr,0,20);
$fuarray=unpack("nint",substr($returnstr,20,4));
//其中n是类型,与pack相同,int是保存的变量脚标
$age = $fuarray['int'];
//age = 33

对于char类型的字符可以直接使用sprintf转换,具体使用方法请看

https://www.w3school.com.cn/php/func_string_sprintf.asp

其中pack和unpack想看更好地例子可以到w3chool上去看看。

https://www.w3school.com.cn/php/func_misc_unpack.asp

在php转换结构体的时候千万要注意字节对齐问题。

好了,我们现在我们有了方法,写个例子测试一下,首先完成一下linux的程序

send.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct{
    long type;
    char name[20];
    int age;
}Msg;

int main()
{
    key_t key = ftok("/home",'6');
    printf("key:%x\n",key);

    int msgid = msgget(key,IPC_CREAT|O_WRONLY|0777);
    if(msgid<0)
    {   
        perror("msgget error!");
        exit(-1);
    }   

    Msg m;
    m.type = 6;
    memset(m.name, 0, 20);
    memcpy(m.name, "hello!", 7);
    m.age = 22;
    
    msgsnd(msgid,&m,sizeof(m)-sizeof(m.type),0);

    return 0;
}

recv.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct{
    long type;
    char name[20];
    int age;
}Msg;

int main()
{
    key_t key = ftok("/home",'6');
    printf("key:%x\n",key);

    int msgid = msgget(key,O_RDONLY|IPC_CREAT|0777);
    if(msgid<0)
    {   
        perror("msgget error!");
        exit(-1);
    }   

    Msg rcv;
    long type = 6;

    msgrcv(msgid,&rcv,sizeof(rcv)-sizeof(type),type,0);
    printf("rcv--name:%s age:%d\n",rcv.name,rcv.age);

    msgctl(msgid,IPC_RMID,NULL);
    return 0;
}

运行结果如下:

$ ./send
key:36020001

$ ./recv
key:36020001
rcv--name:hello! age:22

没有问题,下面我们要修改php代码,让php也能发送接收结构体。

php支持结构体发送

send.php

<?php 
$message_queue_key = ftok("/home",'6'); 
$message_queue = msg_get_queue($message_queue_key, 0777); 

$name = "Hello";
$age = 33;
$msg = sprintf("%-20s",$name).pack("l",$age);
msg_send($message_queue, 6, $msg, FALSE); 
?> 

使用linux程序接收的结果:

$ ./recv
key:36020001
rcv--name:Hello               ! age:33

这里有一个问题,就是接收到的字符串很长,并且有“!”,这里说明一下,因为php使用的spritf进行的填充,默认填充的空格,所以字符串没有结束符“/0”,导致字符串很长,那么那个!是什么呢,就是那个年龄33。

php支持结构体接收

recv.php

<?php 
$message_queue_key = ftok("/home",'6'); 
$message_queue = msg_get_queue($message_queue_key, 0777); 

//从消息队列中读 
msg_receive($message_queue, 6, $message_type, 30, $message, FALSE); 
$name = substr($message,0,20);
$fuarray = unpack("lint",substr($message,20,4));
$age = $fuarray['int'];
print_r("php rcv--name:".$name."  age:".$age."\n"); 
msg_remove_queue($message_queue); 
?> 

执行结果:

$ php ./recv.php 
php rcv--name:hello!  age:22

好了,基本就是这些了,这个只是最最基本的东西,剩下的还需要自己去完善。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值