分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > IT知识

【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)

发布时间:2023-09-06 01:45责任编辑:沈小雨关键词:PHP

原文:http://blog.csdn.net/zhang197093/article/details/78143687?locationNum=6&fps=1

--------------------------------------------------------------------------------------------------------------------------------------------------------

Socket API一开始是为了解决网络通讯而设计的,而后来在此之上又衍生出一种叫做本地套接字(Unix Domain Socket)的技术,本地套接字顾名思义,只支持本地的两个进程之间进行通信,虽然网络套接字(Internet Domain Socket)也可以通过本地回环地址(127.0.0.1)来实现本地进程间通信,但由于本地套接字不需要经过网络协议栈,封包拆包、计算校验和等操作,所以效率上相比网络套接字有一定的优势。由于本地套接字性能高、稳定、支持非血缘关系的进程间通讯,所以本地套接字也是当下使用最广泛的IPC(进程间通信)的机制之一。

Nginx 与 PHP-FPM 之间使用网络套接字(127.0.0.1:9000)和使用本地套接字两种通信方式的性能对比

一般我们都是让 PHP-FPM 监听 127.0.0.1:9000 ,显然这时 Nginx 与 PHP-FPM 是通过网络套接字来实现通讯的,其实,如果 Nginx和 PHP-FPM运行在同一台服务器上,我们还可以让 PHP-FPM监听本地套接字,接下来就针对这两种方式的性能做一简单的比较。

这里我的Nginx开启两个worker进程

[plain]view plaincopy
  1. [root@localhost~]#ps-ef|grepnginx
  2. root18381022:48?00:00:00nginx:masterprocess/usr/sbin/nginx-c/etc/nginx/nginx.conf
  3. nginx18391838022:48?00:00:00nginx:workerprocess
  4. nginx18401838022:48?00:00:00nginx:workerprocess
  5. root18511797022:49pts/000:00:00grepnginx

使用网络套接字,Nginx和PHP-FPM配置分别如下:

压力测试test.php脚本

[php]view plaincopy
  1. <?php
  2. phpinfo();
压测结果:
再来看看 Nginx和PHP-FPM 用本地套接字方式的通信,Nginx 和 PHP-FPM 的配置稍作修改:
压测结果:以上测试都是多次压测后取得结果,从结果可以看到,本地套接字要比网络套接字的QPS平均高了100多,和我们预期基本一致。

PHP的本地套接字编程

其实PHP的本地套接字编程和网络套接字基本一致,只是传的参数不一样。

PHP为socket编程提供了两套API,一套是 socket_* 系列方法,这在我们前面的系列文章里演示过了,另一套是 stream_socket_* 系列方法,而后者使用起来更加的方便,这里我们采用后者来演示。

stream_socket_* 方法列表:

[plain]view plaincopy
  1. ?stream_socket_accept—接受由stream_socket_server创建的套接字连接
  2. ?stream_socket_client—OpenInternetorUnixdomainsocketconnection
  3. ?stream_socket_enable_crypto—Turnsencryptionon/offonanalreadyconnectedsocket
  4. ?stream_socket_get_name—获取本地或者远程的套接字名称
  5. ?stream_socket_pair—创建一对完全一样的网络套接字连接流
  6. ?stream_socket_recvfrom—Receivesdatafromasocket,connectedornot
  7. ?stream_socket_sendto—Sendsamessagetoasocket,whetheritisconnectedornot
  8. ?stream_socket_server—CreateanInternetorUnixdomainserversocket
  9. ?stream_socket_shutdown—Shutdownafull-duplexconnection

具体方法的使用请参阅PHP手册,这里直接演示代码:

server端代码:

[php]view plaincopy
  1. <?php
  2. //stream_server.php
  3. $sockfile=‘/dev/shm/unix.sock‘;
  4. //如果sock文件已存在,先尝试删除
  5. if(file_exists($sockfile))
  6. {
  7. unlink($sockfile);
  8. }
  9. $server=stream_socket_server("unix://$sockfile",$errno,$errstr);
  10. if(!$server)
  11. {
  12. die("创建unixdomainsocketfail:$errno-$errstr");
  13. }
  14. while(1)
  15. {
  16. $conn=stream_socket_accept($server,5);
  17. if($conn)
  18. {
  19. while(1)
  20. {
  21. $msg=fread($conn,1024);
  22. if(strlen($msg)==0)//客户端关闭
  23. {
  24. fclose($conn);
  25. break;
  26. }
  27. echo"readdata:$msg";
  28. fwrite($conn,"readok!");
  29. }
  30. }
  31. }
  32. fclose($server);


client端代码:
[php]view plaincopy
  1. <?php
  2. //stream_client.php
  3. $client=stream_socket_client("unix:///dev/shm/unix.sock",$errno,$errstr);
  4. if(!$client)
  5. {
  6. die("connecttoserverfail:$errno-$errstr");
  7. }
  8. while(1)
  9. {
  10. $msg=fread(STDIN,1024);
  11. if($msg=="quit\n")
  12. {
  13. break;
  14. }
  15. fwrite($client,$msg);
  16. $rt=fread($client,1024);
  17. echo$rt."\n";
  18. }
  19. fclose($client);

运行

server端:

[plain]view plaincopy
  1. [root@localhosthtml]#phpstream_server.php
  2. readdata:hellounixdomainsocket
  3. readdata:areyouok?
  4. readdata:I‘mfine!
  5. PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
  6. PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
  7. PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
  8. PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
  9. PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
  10. PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13

client端:
[plain]view plaincopy
  1. [root@localhosthtml]#phpstream_client.php
  2. hellounixdomainsocket
  3. readok!
  4. areyouok?
  5. readok!
  6. I‘mfine!
  7. readok!
  8. ^C

以上是一个最简单的本地套接字的代码演示,细心的读者可能注意到了server端报的warning,服务器如果长时间没有客户端过来连接,超过了stream_socket_accept 方法设置的timeout,服务器端便会报这个警告,事实上,真正的服务端代码是不会是像这样写的,因为这种方式同一时间只能处理一个客户端连接,如果要实现并发,一种方式就是使用IO多路复用,如同 socket_* 系列方法中有socket_select 方法 (参考系列文章第一篇http://blog.csdn.net/zhang197093/article/details/77366407),stream_socket_* 系列方法提供了 stream_select 方法来实现多路复用,使用方法也很相似。

[php]view plaincopy
  1. intstream_select(array&$read,array&$write,array&$except,int$tv_sec[,int$tv_usec=0])

The stream_select() function accepts arrays of streams and waits for them to change status. Its operation is equivalent to that of the socket_select() function except in that it acts on streams.

详细的方法介绍请参考PHP手册:http://php.net/manual/zh/function.stream-select.php

优化后的代码如下:

[php]view plaincopy
  1. <?php
  2. //stream_server.php
  3. $sockfile=‘/dev/shm/unix.sock‘;
  4. //如果sock文件已存在,先尝试删除
  5. if(file_exists($sockfile))
  6. {
  7. unlink($sockfile);
  8. }
  9. $server=stream_socket_server("unix://$sockfile",$errno,$errstr);
  10. if(!$server)
  11. {
  12. die("创建unixdomainsocketfail:$errno-$errstr");
  13. }
  14. $listen_reads=array($server);
  15. $listen_writes=array();
  16. $listen_excepts=NULL;
  17. while(1)
  18. {
  19. $can_reads=$listen_reads;
  20. $can_writes=$listen_writes;
  21. $num_streams=stream_select($can_reads,$can_writes,$listen_excepts,0);
  22. if($num_streams)
  23. {
  24. foreach($can_readsas&$sock)
  25. {
  26. if($server==$sock)
  27. {
  28. $conn=stream_socket_accept($server,5);//此时一定存在客户端连接,不会有超时的情况
  29. if($conn)
  30. {
  31. //把客户端连接加入监听
  32. $listen_reads[]=$conn;
  33. $listen_writes[]=$conn;
  34. }
  35. }
  36. else
  37. {
  38. $msg=fread($sock,1024);//此时一定是可读的
  39. if(strlen($msg)==0)//读取到0个字符,说明客户端关闭
  40. {
  41. fclose($sock);
  42. //从sock监听中移除
  43. $key=array_search($sock,$listen_reads);
  44. unset($listen_reads[$key]);
  45. $key=array_search($sock,$listen_writes);
  46. unset($listen_writes[$key]);
  47. echo"客户端关闭\n";
  48. }
  49. else
  50. {
  51. echo"readdata:$msg";
  52. //是否可写
  53. if(in_array($sock,$can_writes))
  54. {
  55. fwrite($conn,"readok!");
  56. }
  57. }
  58. }
  59. }
  60. }
  61. }
  62. fclose($server);

此时这个server就不会有前面那个Warning了,并且支持并发
[plain]view plaincopy
  1. [root@localhosthtml]#phpstream_server.php
  2. readdata:helloworld
  3. readdata:hellounixdomainsocket
  4. readdata:harryup
  5. readdata:
  6. readdata:
  7. readdata:I‘manotherclient
  8. 客户端关闭
  9. 客户端关闭
  10. readdata:I‘mthethirdclient
  11. 客户端关闭

That‘s all!

版权声明:本文为博主原创文章,未经博主允许不得转载。 //blog.csdn.net/zhang197093/article/details/78143687

【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)

原文地址:https://www.cnblogs.com/oxspirt/p/8568835.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved