C语言与PHP与JavaScript通信
本来只是谈谈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; } |
下载精减代码(无任何功能,不能直接运行,但是功能片基本完整,下一步可能加进一个简单例子):
欢迎与我交流。
吴老师提到网页和服务器之间的通信,我才发现过去的项目没有及时整理,差点要弄丢。
Leave a comment