Heim >Backend-Entwicklung >PHP-Tutorial >PHP的fsockopen函数详解

PHP的fsockopen函数详解

WBOY
WBOYOriginal
2016-06-23 13:18:011355Durchsuche

先来看看手册是如何定义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模拟访问接口往往比浏览器访问同样的接口慢很多。这个问题困扰过我很久,今天终于找到原因了。我看网上很多朋友有同样的问题,分享出来供大家参考。

我们常常写这样的代码:

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

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也把连接保存下来那是不是就不用建立连接了?答案是肯定的:第一次访问接口时使用pfsockopen(pfsockopen与fsockopen唯一的区别就是它建立的是长连接)函数建立与服务器的连接,在访问完成后不关掉(fclose)连接,以后的访问就直接使用这个连接。具体到代码里就是:先判断有没有连接,如果有,继续用,如果没有,建立pfsockopen连接。

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

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


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn