PHP5.4 pfsocketopen函数判断sock是否存活的bug(由memcached引起)
来源:互联网 发布:数据库实用教程孙飞显 编辑:程序博客网 时间:2024/06/02 02:13
之前遇到的一个业务代码非常奇葩的bug,代码中使用pfsocketopen作为http请求,偶尔会不能读取返回结果。
经过排查,发现PHP5.4版本下判断socket是否存活存在一个bug。
解决方案:
1)在这个情况下,可以不使用长链接,改为使用短连接
2)由于PHP5.4已经不再维护了,可以升级PHP版本到5.6
PS:可以看看我在github上的讨论(https://github.com/php-memcached-dev/php-memcached/issues/313)
去除业务逻辑后,代码如下:
<?php$num = 1;while ($num < 5) { echo ">>>start {$num}", PHP_EOL; $mc = new Memcached(); $mc->addServer('host.com', 11511); $data = $mc->get("freg127.0.0.1"); $socket = pfsockopen("host2.com", 80, $errno, $errstr, 30); echo $socket, PHP_EOL; stream_set_write_buffer($socket, 0); socket_set_timeout($socket, 30); $request = get_post_data(); $len = fputs($socket, $request, strlen($request)); echo "send {$len} data", PHP_EOL; $read = fgets($socket); if (preg_match("/^HTTP/", $read) === 0) { echo "Illegal HTTP server.{$read}", PHP_EOL; exit; } $content_length = 0; while (!feof($socket)) { $read = fgets($socket); if (preg_match("/^Content-Length: (\d+)/", $read, $matches) === 1) { $content_length = intval($matches[1]); } if ($read == "\r\n") { break; } } $read_len = fread($socket, $content_length); echo "read content {$content_length} data", PHP_EOL; echo "sleep 120", PHP_EOL; sleep(120); ++$num;}function get_post_data(){ return <<<DATAPOST /users/api.php HTTP/1.1Host: host2.com:80User-Agent: PHPRPC Client 3.0 for PHPConnection: Keep-AliveCache-Control: no-cacheAccept: */*Accept-Encoding: gzip,deflateContent-Type: application/x-www-form-urlencoded; charset=utf-8Content-Length: 586_omit rpc data_DATA;}
代码在cli环境下执行,期望的输出:
>>>start 1Resource id #4send 850 dataread content 84 datasleep 120>>>start 2Resource id #4send 850 dataread content 84 datasleep 120然而实际的输出为:
>>>start 1Resource id #4send 850 dataread content 84 datasleep 120>>>start 2Resource id #4send 850 dataIllegal HTTP server.第二次调用pfsocketopen的时候,PHP判断为socket是存活的,因此没有进行重新连接。通过对PHP5.4的源码进行分析,发现这个小小的bug:
main/streams/stream.c:1357
PHPAPI int _php_stream_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC){int ret = PHP_STREAM_OPTION_RETURN_NOTIMPL;if (stream->ops->set_option) {ret = stream->ops->set_option(stream, option, value, ptrparam TSRMLS_CC); //here will return PHP_STREAM_OPTION_RETURN_OK}and then
main/streams/xp_socke.c:293
if (sock->socket == -1) { alive = 0;} else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) { if (0 >= recv(sock->socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EWOULDBLOCK) { alive = 0; }}
当上一次php_socket_errno()调用返回EWOULDBLOCK的时候,即使recv调用返回0,仍然会认为socket是存活的,来看看PHP5.6版本的处理:
if (sock->socket == -1) { alive = 0;} else if (php_pollfd_for(sock->socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {#ifdef PHP_WIN32 int ret;#else ssize_t ret;#endif int err; ret = recv(sock->socket, &buf, sizeof(buf), MSG_PEEK); err = php_socket_errno(); if (0 == ret || /* the counterpart did properly shutdown*/ (0 > ret && err != EWOULDBLOCK && err != EAGAIN)) { /* there was an unrecoverable error */ alive = 0; }}
0 0
- PHP5.4 pfsocketopen函数判断sock是否存活的bug(由memcached引起)
- 一个由sscanf函数引起的bug
- JVM判断对象是否存活的方法
- 判断Java对象是否存活的方法
- 判断Java对象是否存活的方法
- jvm-判断对象是否存活的算法
- 判断context是否存活
- jvm判断对象是否存活
- 如何判断对象是否“存活”
- GC判断对象是否存活
- VC判断目标主机是否存活,模拟系统的ping
- VC判断目标主机是否存活,模拟系统的ping
- 判断的当前的app是否还存活
- JVM中判断对象是否存活的方法
- 垃圾收集之判断对象是否存活的算法
- JVM学习笔记(二)JVM判断对象是否”存活”
- 由一个bug引起的关于list的思考
- 一个由模板函数引起的问题
- Ruby on Rails学习笔记(8)--ruby中的方法
- mongodb3.4 安装及用户名密码设置
- Interface Builder could not open the document "xxx.xib" because it does not exist.
- uCOS操作系统
- 计算机基础知识整理
- PHP5.4 pfsocketopen函数判断sock是否存活的bug(由memcached引起)
- Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法
- 进程和线程的区别
- linux find命令
- 蓝桥杯 算法训练 纪念品分组
- mongodb单机搭建
- 安卓通过ContentResolver添加联系人
- 浅谈线段树
- ffmpeg 转换x264到Fragmented MP4