Socket 深度探究 4 PHP (三)

2024-08-30 09:08
文章标签 php 深度 socket 探究

本文主要是介绍Socket 深度探究 4 PHP (三),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

来源URL:http://blog.csdn.net/shagoo/article/details/6647961


看过前两篇文章《Socket深度探究4PHP(一)》和《Socket深度探究4PHP(二)》,大家应该对目前 Socket 技术的底层有了一定的了解。本文我们会对 PHP-5.3.6 的源码中的 Socket 模块进行一定的分析,然后再简单介绍一下目前比较热门的一些相关技术,比如 Node.js 等。

自 PHP4 之后,越来越多的模块都被作为扩展提取出来(可单独编译),都在 PHP 源码的 ext 目录下面,因此我们我需要先进入 ext/sockets/ 目录,做过 PHP 扩展的同学应该都很熟悉下面的一些文件了,这次我们主要分析的是 php_sockets.h 和 sockets.c 这两个 C 源码文件。

ext/sockets/php_sockets.h

这个头文件很简单,我们主要看一下下面列出的几个重点:

32 行:

[cpp]  view plain copy
  1. #ifdef PHP_WIN32  
  2. #include <winsock.h>  
  3. #else  
  4. #if HAVE_SYS_SOCKET_H  
  5. #include <sys/socket.h>  
  6. #endif  
  7. #endif  

以上就是 PHP 对于不同环境 Socket 底层调用的定义了,我们可以看到不管是 Unix 还是 Windows 环境,PHP均调用的是系统标准的 BSD Socket 库。然后我们看下面这个重要的结构体定义:

82 行:
[cpp]  view plain copy
  1. typedef struct {  
  2.     PHP_SOCKET bsd_socket;  
  3.     int        type;  
  4.     int        error;  
  5.     int        blocking;  
  6. } php_socket;  

这个就是 php socket 的存储结构了,此结构体在以下的代码阅读中将会大量出现,里面的几个字段很容易理解:bsd_socket 就是标准的 socket 类型,type 表示 socket 类型(PF_UNIX/AF_UNIX),error 是错误代码,blocking 则表示是否阻塞。

ext/sockets/sockets.c

这个文件比较长,为了直接切入重点,我们会按照《 Socket 深度探索 4 PHP (一)  》中 select_server.php 部分代码来按顺序分析一下在最经典的 select 模式中我们用到的主要方法:

>socket_create_listen

859 行:PHP_FUNCTION(socket_create_listen)
这个函数很简单,初始化 php_sock 并获取 socket 需要监听的端口,然后传入下面的 php_open_listen_sock 函数进行加工,最后调用 ZEND_REGISTER_RESOURCE 宏返回 php_sock。

347行:static int php_open_listen_sock(php_socket **php_sock, int port, int backlog TSRMLS_DC)
此函数基本上就是 socket 的标准初始化过程:socket(...) -> bind(...) -> listen(...)(详见 368 行至 391 行)。
[cpp]  view plain copy
  1. sock->bsd_socket = socket(PF_INET, SOCK_STREAM, 0);  
  2. sock->blocking = 1;  
  3. ...  
  4. sock->type = PF_INET;  
  5. ...  
  6. if (bind(sock->bsd_socket, (struct sockaddr *)&la, sizeof(la)) != 0) {  
  7. ...  
  8. }  
  9. if (listen(sock->bsd_socket, backlog) != 0) {  
  10. ...  
  11. }  

>socket_set_nonblock

906 行:PHP_FUNCTION(socket_set_nonblock)
这个函数也很简单,从 ZEND_FETCH_RESOURCE 取出 runtime 中的 php_sock 然后调用 php_set_sock_blocking 函数来设置 sockfd 的阻塞或者非阻塞(此函数可以参考 main/network.c 第 1069 行,我们可以看到 PHP 是使用 fcntl 函数来设置的)。

>socket_select

785 行:PHP_FUNCTION(socket_select)
也是标准的 select 函数调用,过程如下:FD_ZERO(...) -> php_sock_array_to_fd_set(...) -> select(...) -> php_sock_array_from_fd_set(...),可能比较特殊的就是 php_sock_array_from_fd_set() 和 php_sock_array_from_fd_set() 两个函数,这是由于我们要先把 PHP 的 fd 数组转换成原生 fd 集合,才能调用原生的 select 函数,而最后系统还把 fd 集合重新转回到 PHP 的 fd 数组(具体代码参考 799 行至 851 行)。

>socket_accept

881 行:PHP_FUNCTION(socket_accept)
此函数基本上也就是 socket 原生 accept 函数的包装,具体代码可参考 397 行:php_accept_connect 函数中的逻辑,最后调用 ZEND_REGISTER_RESOURCE 宏返回 new_sock,若失败程序会清理使用的 out_socket 资源。

>socket_write

986 行:PHP_FUNCTION(socket_write)
按照以上的思路看这个函数也非常简单,详见 986 行,唯一值得注意的是对于不同操作系统调用的函数有点不同,代码(见 1004 行)如下:
[cpp]  view plain copy
  1. #ifndef PHP_WIN32  
  2.     retval = write(php_sock->bsd_socket, str, MIN(length, str_len));  
  3. #else  
  4.     retval = send(php_sock->bsd_socket, str, min(length, str_len), 0);  
  5. #endif  

>socket_read

1021 行:PHP_FUNCTION(socket_read)
此函数是用于接受 socket 的数据,调用的原生函数是 recv(),不过这里需要注意的是 PHP 为我们提供两种获取方式:
1、PHP_NORMAL_READ
按行读取,具体代码见 419 行:php_read 函数的逻辑,我们注意到此函数在非阻塞模式下会立即返回,否则将会读取直至遇到 \n 或者 \r 字符。
2、PHP_BINARY_READ
代码见 1045 行:retval = recv(php_sock->bsd_socket, tmpbuf, length, 0); 相当原生和“环保”。
最后,如果返回值为 -1 则会进行一些错误记录和系统清理工作。

>socket_close

970 行:PHP_FUNCTION(socket_close)
清理 socket 运行时所用的资源。

>socket_shutdown

1968 行:PHP_FUNCTION(socket_shutdown)
调用原生 shutdown 函数来关闭 socket。

分析下来,PHP 的 socket 模块中绝大部分的代码还是使用的是系统标准的原生 socket 库,其中唯一有可能造成性能隐患的就是 select 中 PHP 的 fd 数组与原生 fd 集合转换,至于其他的一些简单的数据拷贝基本对效率不会有什么影响。总的来说,PHP 的 socket 模块应该效率还是比较高的,但是在使用的时候还是需要注意到一些资源的及时释放,因为毕竟是 Daemon 程序,需要不断运行的,而且 PHP 的数据结构是很占内存(是原生 C 的 4 倍左右)的。

node.js

最后,我们看看现在很流行的 Node.js( http://nodejs.org/ ),它采用了 JavaScript 的语言引擎,语法非常的简洁,对闭包的完美支持让它特别适合做异步 IO 的代码编写,下面是一个最简单的 HTTP Server,只用仅仅六行代码:
[javascript]  view plain copy
  1. var http = require('http');  
  2. http.createServer(function (req, res) {  
  3.   res.writeHead(200, {'Content-Type''text/plain'});  
  4.   res.end('Hello World\n');  
  5. }).listen(8000, "127.0.0.1");  
  6. console.log('Server running at http://127.0.0.1:8000/');  

运行起来感受一下,有没有惊艳的感觉啊?事实上用它来写一些简单的服务确实很不错,有兴趣的朋友可以多研究研究(中文社区: http://cnodejs.org/ ),它有 8000 行 C++ 代码,2000 行 javascript 代码,使用 Google 的 V8 引擎(和 Mongodb 一样),相当的很小巧精悍。下面是我在使用过程总结出中几个要点,大家可以参考:

1、使用 V8 引擎(和 Mongodb 一样),内置 JSON,代码简洁,使用方便。
2、使用单线程非阻塞 I/O 中的 select 方式,比较稳定(但是对于超高并发有点力不从心)。
3、一些第三方应用接口不是很稳定,比如 Mongodb 的接口,并发 200 出现卡死现象,Mysql 接口也比 fast-cgi 差很多。
4、注意使用 try{...}catch{...} 来捕获错误;使用 process.on('uncaughtException', function(err){...}); 来处理未捕获的错误,否则出错会导致整个服务退出。

当然,Node.js 还在不断的更新发展中,虽然目前我在公司的服务架构中还不敢使用它,我还是很希望它能够迅速成长起来,这样子我们开发服务中间件的时候,就会多出一个很棒的选项啦~

这篇关于Socket 深度探究 4 PHP (三)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1120383

相关文章

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

最新Spring Security实战教程之表单登录定制到处理逻辑的深度改造(最新推荐)

《最新SpringSecurity实战教程之表单登录定制到处理逻辑的深度改造(最新推荐)》本章节介绍了如何通过SpringSecurity实现从配置自定义登录页面、表单登录处理逻辑的配置,并简单模拟... 目录前言改造准备开始登录页改造自定义用户名密码登陆成功失败跳转问题自定义登出前后端分离适配方案结语前言

Redis 内存淘汰策略深度解析(最新推荐)

《Redis内存淘汰策略深度解析(最新推荐)》本文详细探讨了Redis的内存淘汰策略、实现原理、适用场景及最佳实践,介绍了八种内存淘汰策略,包括noeviction、LRU、LFU、TTL、Rand... 目录一、 内存淘汰策略概述二、内存淘汰策略详解2.1 ​noeviction(不淘汰)​2.2 ​LR

Python与DeepSeek的深度融合实战

《Python与DeepSeek的深度融合实战》Python作为最受欢迎的编程语言之一,以其简洁易读的语法、丰富的库和广泛的应用场景,成为了无数开发者的首选,而DeepSeek,作为人工智能领域的新星... 目录一、python与DeepSeek的结合优势二、模型训练1. 数据准备2. 模型架构与参数设置3

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操