C语言与PHP与JavaScript通信

2012-03-29 10:25 / no comment / 57 views /

本来只是谈谈C语言与PHP的通信,但是接着谈到JavaScript的通信几乎是水到渠成的事。实现C语言与PHP通信的方式不只一个,有共享内存、Socket、CGI,还可以为PHP写插件等。但是无论哪种通信方式,它都只是一种通信,不应该成为逻辑的主角。在我最初研究这种通信的时候,网上还没有资料,我在PHP中看到了共享内存,马上就试验了共享内存的通信方式,证实可行。但是这种通信需要依赖信号量实现互斥,才能实现完整的通信线路,而PHP的信号量机制和C似乎不太兼容。我认为最简单易行的通信应该选用socket,这样可以轻易调整规模。以前没有整理,资料都快要遗失。现在我重新整理一下项目中用到的C语言与PHP通信的通信模块。

一、通信接口设计

1) C与PHP的封装

PHP属于前台,C运行于后台。命令几乎总是从前台传到后台,然后返回执行结果。所以只设计了在PHP中运行C命令的接口。

1
function run_php_cmd($cmd);

函数以命令做为参数,返回执行结果。

C语言部分为了方便,也实现了一个命令接口。

1
void do_cmd(char *cmd, char *output, int format);

该函数从一个cmd_list命令列表中查找命令、运行、并返回相应格式的执行结果。

1
2
3
4
5
6
7
8
9
10
11
static cmd_line_t cmd_list[] = {
    {"getdev", getdev},
    {"adddev", adddev},
    {"deldev", deldev},
    {"getports", getports},
    {"addnod",addnod},
    {"getnod",getnod},
    {"delnod",delnod},
    {"setviewpoint",setviewpoint},
    {"starttest",starttest}
};

cmd_line为一个如下的结构体:

1
2
3
4
typedef struct cmd_line{
    char cmd[32];
    void (* function)(int argc, char *argv[], char * output, int format);
}cmd_line_t;

那么在C语言部分,如果要增加一个新的命令支持,只需要实现一个支持函数,并给一个相应的名字,填入cmd_list列表就可以了。

例如,增加example命令,实现一个函数:

1
void example(int argc, char *argv[], char * output, int format);

在cmd_list增加一行:

1
2
3
4
static cmd_line_t cmd_list[] = {
    //example
    {"example",example}
};

这样就很方便的添加了一个命令支持。

2) 从前端JavaScript直达C的通信线路

实现了php到c的通信线路经常不是最终目的,所以我还设计了直接从JavaScript到达C语言的通信线路。这样只需要在js端和c语言端实现相应的功能即可了,中间的通信线路一旦架设好就再也不用理它。

js中的函数接口为:

1
function do_cmd(cmd, callbackfunction);

只用在js的do_cmd函数中填入命令和回调函数即可。

如,运行example命令:

1
2
3
function do_cmd('example', function(result){
//do your action
});

可以在C语言端以json的格式返回数据,在js端以json的格式处理数据。json是一种设计相当优秀的数据格式,你可以用它的库处理,但是不用库也能非常简单方便的实现。

3) 通信线路设计小结

现在我们不管具体实现,设计好了一个从js到c的通信线路,前后端的命令设计和代码工作都可以大量的并行进行了。这里似乎不关php什么事了,php只是起到了js和c之间的中间人的作用。那么如果能实现在c中直接实现http协议,php部分也可以省掉了。但是我不知道有没有c语言的http库,可能下一步会查查看。而Go语言和nodejs这两种现代语言都有强硬的http库,犀利无比,所以后面的精力可以更愿意花到学习这两门语言上。我熟练犀利的C,难道要放下了吗?不舍,应该也不会,C已经几乎用到了毫无障碍的境界,虽然感受到了诸多麻烦的地方,也深知道它的强大,放弃太可惜了。

这种通信线路只是接口,不只一种实现方式,最初我用的是共享内存,但是这种方式受不了一丁点的压力,原因在于PHP的信号量机制。后来改到socket,但是接口没有做任何调整,基于此接口的代码完全不需要做修改。

二、通信的实现

实现的工作已经全部在代码里了,只简要说明一下。这里可能有反复的socket连接,和http连接,是效率不高的地方,但是效率经常是我考虑的下一步。清晰的架构一直是我最优先考虑的。况且这两个低效的问题想要解决需要用到大量另外的知识,暂不考虑。

PHP部分run_php_cmd函数的功能就是建立一个到c的socket连接,把命令传过去,把C的返回值输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
function run_php_cmd($cmd){

    $sockfd = connect_sock();  
    $out = '';    
 
    socket_write($sockfd, $cmd, strlen($cmd));  
   
    if($out = socket_read($sockfd, 2048))  {    
        echo $out;
        }  
    socket_close($socket);  
}

function connect_sock(){
    $service_port = 2012;
    $address = "127.0.0.1";

    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);  
    if ($socket >= 0)  
    {
        $result = socket_connect($socket, $address, $service_port);  
        if ($result >= 0)  
            return $socket;
    }
}
?>

做一个ajax.php的文件,供js调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
    header("content-type:text/html; charset=utf-8");
    include_once('functions.php');
   
    if (isset($_GET['q'])) {
        $cmd = $_GET['q'];
    }
    else if (isset($_GET['cmd'])) {
        $cmd = $_GET['cmd'];
    }
    else $cmd = "ajax";

    if (isset($_GET['callback'])) {
        $callback = $_GET['callback'];
        $callback .= '(';
        echo($callback);
    }
    run_php_cmd($cmd);

    if (isset($_GET['callback'])) {
        echo(')');
    }
?>

js部分do_cmd函数打开ajax.php文件,把命令用get方式传过去,得到返回值后回调用回调函数,这里为了方便用到jquery库,在网页处理中也用的jquery库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function do_cmd(cmd, callbackfunction){
    tmp = Math.random();
    buf = "{ tmp:'" + tmp+ "'}";
    $.ajax({
        type:"GET",
        url:'ajax.php?q='+cmd,  //ajax.php?q=+cmd相对路径,//为了兼容,而修改,2010.10.09
        dataType:"json",
        data:buf,
        success:callbackfunction,
        error:function(XMLHttpRequest,textStatus,errorThrown){
            alert(textStatus+"  "+errorThrown);
        }
    });
}

c语言部分一个是do command的代码。一个do_cmd_php,一个do_cmd_stdin,分别等待标准输入和php两边的命令,并用do_cmd执行,输出相应的格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
static int devide(char * cmd, char *argv[], int len)
{
    int i = 0;

    do{
        while(*cmd == 32 && *cmd != 0) cmd++;
        argv[i++] = cmd;
        while(*cmd != 32 && *cmd != 0) cmd++;
        if(*cmd) *(cmd++) = 0;
        else return i;
        if(i == len) return i;
    }while(cmd && *cmd);
    return i;
}

static void do_cmd(char *cmd, char *output, int format)
{
    int argc, i;
    char *argv[20];

    if(!(argc = devide(cmd, argv, 20))) return;

    for(i = sizeof(cmd_list)/sizeof(cmd_line_t) - 1; i >= 0; i--){
        if(!strcmp(cmd_list[i].cmd, argv[0])){
            (* (cmd_list[i].function))(argc, argv, output, format);
            break;
        }
    }
}

void do_cmd_php(int sock_id, int event, void * cmd, int len, void * callbackdata)
{
    char output[2048];
    switch(event){
        case SOCK_CONNECT:
            break;
        case SOCK_DATAREADY:
            do_cmd(cmd, output, JSON);
            send_socket(sock_id, output, strlen(output));
            break;
        case SOCK_DISCONNECT:
            break;
    }
}

void do_cmd_stdin(void)
{
    char cmd[100];
    char output[1024];
    int i;
    while(1){
        i=0;
        while((cmd[i]=getchar())!='\n') i++;
        cmd[i]='\0';

        *output = 0;
        do_cmd(cmd, output, PRINT);
        printf("%s", output);
    }
}

其中do_cmd_php的代码比较诡异,因为我对socket通信进行了自己的封装,只需如下代码即可建立php运行的逻辑,异常简单(socket的封装这里暂不做讨论):

1
2
3
4
    if(sock_server_bind(2012,20,do_cmd_php,NULL)){
        printf("php cmd failed\n");
        return -1;
    }

下载精减代码(无任何功能,不能直接运行,但是功能片基本完整,下一步可能加进一个简单例子):

c-php-communication

欢迎与我交流。

吴老师提到网页和服务器之间的通信,我才发现过去的项目没有及时整理,差点要弄丢。

Related Posts

  1. Linux下Socket通信、共享内存和信号量混合使用的例子
  2. 删除一个字符串中的空格
  3. PHP和C语言共享内存通信以及信号量互斥
  4. 线性表
  5. C语言HTML解析器
  6. 读btrecord源代码
  7. Linux下用户态直接读写磁盘扇区
  8. 用于生成RSSFeed的PHP库
  9. Linux下原生异步IO接口Libaio的用法
  10. 通过饭否同步博客副标题

Get a Trackback link

No Comments Yet

You can be the first to comment!

Leave a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>