PHP的fsockopen函数详解

        先来看看手册是如何定义fsockopen函数的。
        fsockopen — 打开一个网络连接或者一个Unix套接字连接。

resource fsockopen ( string $hostname [, int $port = -1 [, int &$errno 
[, string &$errstr [, float $timeout = ini_get("default_socket_timeout") ]]]] )

        使用fsockopen方法和使用CURL方法的实质都是去模拟HTTP协议,所以重点在于如果去构造HTTP协议。

        注意:要使用此函数的功能,需要在PHP.ini文件中打开allow_url_open

//打开连接通道
$fp=fsockopen('www.cnblogs.com',80,$errno,$errstr,30);
if(!$fp){
    die('连接错误'.$errstr);
}
set_time_limit(0);
//通道打开,模拟HTTP请求,http协议标准的换行是\r\n
$http="GET / HTTP/1.1\r\n";
$http.="Host: www.cnblogs.com\r\n";
$http.="User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";
$http.="Connection: Close\r\n\r\n";

//http模拟完成,发送给服务器
fwrite($fp,$http);

//接受服务器的返回结果
$out='';
while(!feof($fp)){
    $out.=fread($fp,1024);
}

echo $out;
fclose($fp);

        在开发过程中常常遇到这样的需求,模拟浏览器访问某接口,并获取返回数据。我们比较常使用的方法是fsockopen与接口建立连接,然后发出指令,然后通过fgets接受返回值。

        但是我们发现,通过PHP模拟访问接口往往比浏览器访问同样的接口慢很多。这个问题困扰过我很久,今天终于找到原因了。我看网上很多朋友有同样的问题,分享出来供大家参考。

        我们常常写这样的代码:

while(!feof($sHnd)) {
    $line = fgets($sHnd, 4096);
}

        fgets会获取文件描述符$sHnd的当前的4096(也可能是别的常数)个字节,如果还没有到4096个字节遇到换行符了,则只返回换行符及换行符之前的内容。

        许多文档教程里也都是这么讲的,这段代码许多情况下也能正常执行。我一步一步跟踪PHP语句的耗时,发现前面若干次的fgets都很快,最耗时的是最后一次fgets,有时长达几秒,有时长达十几秒。

        原来这是服务器的KeepAlive功能造成的,Apache有这么一个设置(nginx等其他web服务器也都有):KeepAlive,如果这个设置置为On,则完成一次请求后,服务器并 不会关闭TCP连接,而是保持连接等待浏览器下次发起其他请求时直接利用这个连接。但是当fgets获取最后一段内容时没有发现换行符,也没有文件结束标志(feof()),所以fgets获取完内容后仍继续等待,希望遇到换行符或者其他内容以达到4096个字符。于是,就这样服务器和PHP耗上了,互相 等待。耗了一会后,服务器先耗不起了,毕竟服务器的连接数很宝贵,当连接若干秒没有活动,就会关掉这个连接(Apache通过KeepAliveTimeout这个选项进行设置,这个值通常为5-15)。服务器关掉连接之后,PHP这边的fgets这才失落得返回最后一批内容, 访问接口过程结束。

        清楚了慢的原因就知道了解决方案了:

服务器返回的HTTP头中包含有内容长度这个属性,当已接受的内容长度与之相等时,我们就可以断定:接口内容已经获取完毕,不必再等了。具体做法 是:每次获取不超过剩余总长度的内容(min(4096, $leftlength))。剩余总长度为0时,跳出while(feof($xxxxx))的循环。

        经过这样的修改,php通过sock方式访问接口速度慢的问题已经基本解决了,但还不够完美,继续速度优化的思路还在KeepAlive上。

        大家都知道访问接口的耗时相当一部分是浪费在建立连接上,如果我们需要频繁调用接口的话,还有很大的优化余地。既然服务器保持了连接,那如果PHP 也把连接保存下来那是不是就不用建立连接了?答案是肯定的:第一次访问接口时使用pfsockopenpfsockopenfsockopen唯一的 区别就是它建立的是长连接)函数建立与服务器的连接,在访问完成后不关掉(fclose)连接,以后的访问就直接使用这个连接。具体到代码里就是:先判断 有没有连接,如果有,继续用,如果没有,建立pfsockopen连接。

        另外,如果接口返回内容比较短(比如:小于50字符)的话,还有优化的余地,那就是在HTTP请求头的Accept-Encoding中去掉 gzip。它的作用是告诉服务器,我(浏览器)可以接受压缩过的内容,如果服务器也支持gzip就压缩后再返回,浏览器得到内容后解压缩再显示。但是如果 内容太短的话,压缩后体积反而会增加,再加上压缩、解压缩的时间,就更加得不偿失了。

        经过以上几步,访问接口速度应该与浏览器一样,理论上还会稍微快一点点。

        原文地址:http://phptianye.blog.51cto.com/10197989/1738500

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值