CURL错误:接收失败:连接被对等方重置 - PHP Curl

109

我遇到了一个奇怪的错误,CURL ERROR: Recv failure: Connection reset by peer

这是发生的情况,如果我没有连接到服务器,突然尝试通过PHP中的CURL连接到服务器,我会收到这个错误。当我再次运行CURL脚本时,错误消失了,然后整个时间都正常工作,如果我让远程服务器闲置约30分钟或重新启动远程服务器并尝试再次连接,则会再次出现错误。因此,似乎连接处于空闲状态,然后突然服务器唤醒,然后工作,然后再次休眠。

这是我的CURL脚本:

$url = Yii::app()->params['pdfUrl'];
$body = 'title='.urlencode($title).'&client_url='.Yii::app()->params['pdfClientURL'].'&client_id='.Yii::app()->params['pdfClientID'].'&content='.urlencode(htmlentities($content));

$c = curl_init ($url);
$body = array(
    "client_url"=>Yii::app()->params['pdfClientURL'],
    "client_id"=>Yii::app()->params['pdfClientID'],
    "title"=>urlencode($title),
    "content"=>urlencode($content)
);
foreach($body as $key=>$value) { $body_str .= $key.'='.$value.'&'; }
rtrim($body_str,'&');

curl_setopt ($c, CURLOPT_POST, true);
curl_setopt ($c, CURLOPT_POSTFIELDS, $body_str);
curl_setopt ($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt ($c, CURLOPT_CONNECTTIMEOUT , 0);
curl_setopt ($c, CURLOPT_TIMEOUT  , 20);

$pdf = curl_exec ($c);
$errorCode = curl_getinfo($c, CURLINFO_HTTP_CODE);
$curlInfo = curl_getinfo($c);
$curlError = curl_error($c);

curl_close ($c);

我完全没有想法和解决方案,请帮忙,非常感激!!!

如果我使用详细输出来查看发生了什么

curl_setopt ($c, CURLOPT_VERBOSE, TRUE);
curl_setopt($c, CURLOPT_STDERR, $fp); 

我得到了以下内容。
* About to connect() to 196.41.139.168 port 80 (#0)
*   Trying 196.x.x.x... * connected
* Connected to 196.x.x.x (196.x.x.x) port 80 (#0)
> POST /serve/?r=pdf/generatePdf HTTP/1.1
Host: 196.x.x.x
Accept: */*
Content-Length: 7115
Content-Type: application/x-www-form-urlencoded
Expect: 100-continue

* Recv failure: Connection reset by peer
* Closing connection #0
012 20:23:49 GMT
< Server: Apache/2.2.15 (CentOS)
< X-Powered-By: PHP/5.3.3
< Connection: close
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=UTF-8
< 
* Closing connection #0

我已经添加了以下内容以删除默认标题,但仍然没有成功:

curl_setopt ($c, CURLOPT_HTTPHEADER, array( 'Expect:' ) );

> Accept: */* Content-Length: 8414 Content-Type:
> application/x-www-form-urlencoded
> 
> * Recv failure: Connection reset by peer
> * Closing connection #0 r: Apache/2.2.15 (CentOS) < X-Powered-By: PHP/5.3.3 < Connection: close < Transfer-Encoding: chunked <
> Content-Type: text/html; charset=UTF-8 < 
> * Closing connection #0

当您在浏览器中输入此URL时会发生什么? - Explosion Pills
@tandu - 我无法将整个URL添加到地址栏,因为内容是大量的HTML文本。但是,当我在浏览器中添加不带POST值的URL时,浏览器可以正常打开页面。 - Elitmiar
URL是http还是https?当使用HTTPS时,您应该确保CURL已准备好使用CURLOPT_SSL_VERIFYPEER设置为false来处理它。 - alganet
1
@Roland,还有一个CURLOPT_SSL_VERIFYHOST,请尝试将其设置为false。 - alganet
1
@Roland:请尝试增加连接超时时间:curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 10); - Jens Bradler
显示剩余13条评论
11个回答

140

介绍

远程服务器向您发送了一个RST数据包,它表示立即断开连接,而不是通常的握手。

可能的原因

A. TCP / IP

这可能是一个TCP/IP问题,您需要与主机解决或升级操作系统,大多数情况下连接在完成下载内容之前就会关闭,导致Connection reset by peer.....

B. 内核错误

请注意,在v2.6.17之后的某些Linux内核上存在一些关于TCP窗口缩放的问题。有关更多信息,请参见以下错误报告:

https://bugs.launchpad.net/ubuntu/+source/linux-source-2.6.17/+bug/59331

https://bugs.launchpad.net/ubuntu/+source/linux-source-2.6.20/+bug/89160

C. PHP和CURL错误

您正在使用PHP/5.3.3,其中也存在一些严重的错误...我建议您使用更高版本的PHPCURL

https://bugs.php.net/bug.php?id=52828

https://bugs.php.net/bug.php?id=52827

https://bugs.php.net/bug.php?id=52202

https://bugs.php.net/bug.php?id=50410

D. 最大传输单元

这个错误的一个常见原因是网络连接上传输的数据包的MTU(最大传输单元)大小已从默认值1500字节更改。 如果您配置了VPN,则在配置期间必须更改此设置。

E. 防火墙:iptables

如果您不了解这些内容,它们可能会导致一些严重问题...尝试访问您正在连接的服务器以检查以下内容

  • 您可以访问该服务器上的80端口

示例

 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT`
  • 以下内容位于最后一行,而不是在任何其他“接受”之前。

示例

  • The Following is at the last line not before any other ACCEPT

Example

  -A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited 
  • 检查所有的DROP, REJECT,确保它们不会阻止你的连接

  • 临时允许所有连接并查看是否可以通过

实验

在不同的服务器或远程服务器上尝试(在线有很多免费的云托管),并测试相同的脚本。如果可以工作,则我的猜测是正确的...您需要更新您的系统

其他代码相关

A. SSL

如果Yii::app()->params['pdfUrl']是一个带有https的URL,而没有包括适当的SSL设置,可能会导致旧版本的curl出现此错误。

解决方案:确保已安装和启用OpenSSL,然后将此代码添加到您的代码中。

curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, false);

1
问题似乎已经解决,但在给出答案之前,我会在接下来的几天内进行检查。我所做的是以下操作,但我不明白为什么这似乎解决了问题。我通过ssh连接到机器并运行了以下命令:sudo iptables -L,然后sudo service iptables stop,然后sudo service iptables start,当我运行sudo service iptables stop时,它清除了我从未使用过的iptables,突然问题得到了解决。iptables中没有信息。您知道这为什么解决了问题吗? - Elitmiar
所以有一个防火墙 :) 它很可能对会话超时的执行方式与网页服务器不同。 - EricM
@Baba - 几天后我会确认它是否真的有效,并回复给你们。 - Elitmiar
@Baba - 感谢您的帮助,最终解决我的问题的方法是在另一台服务器上实现它,这不知怎么地解决了问题。感谢您真正尽心尽力地帮助。 - Elitmiar
在我的情况下,ping 是可以工作的,telnet 也是可以工作的,但是通过 host:nodeporta 连接的两个 kubernetes 集群之间的 http 请求却无法工作。解决方案是向负责公司生产环境安全管理的 3-4 个团队询问,发现是防火墙限制导致通信被阻止。这给了我提示开始推动工单。非常感谢。 - Shondeslitch
显示剩余3条评论

19

通常这个错误表示已经与服务器建立了连接,但连接已被远程服务器关闭。这可能是由于服务器缓慢、远程服务器出现问题、网络问题或者(可能性不大)发送到远程服务器的数据存在安全问题导致的。

通常情况下,网络错误会在一段时间内自行解决,但看起来您已经给它留了足够的时间。

cURL有时会遇到SSL和SSL证书方面的问题。我认为你的Apache和/或PHP是使用最新版本的cURL和cURL SSL库编译的,而且我认为你的Web服务器中没有安装OpenSSL。

虽然我不能确定,但我认为cURL在处理SSL证书方面历史上一直存在问题,而OpenSSL则没有。

无论如何,尝试在服务器上安装OpenSSL并重试,这应该可以帮助您摆脱这个错误。


6

我曾经遇到过一个类似的错误,但是情况有所不同。

当你使用特定的SSL协议来curl一个页面时。

curl --sslv3 https://example.com

如果目标服务器不支持 --sslv3,则会出现以下错误:

curl: (35) TCP connection reset by peer

如果将支持的协议用于连接,则此错误将消失。
curl --tlsv1.2 https://example.com

如果服务器配置为仅响应https,则仅从http切换到https可能已足够。 - TomEberhard

5
那么Yii::app()->params['pdfUrl']返回的URL是什么?你说它应该是https,但日志显示它连接的是80端口...几乎没有服务器设置为接受https连接。cURL足够聪明,知道https应该在443端口上...这表明你的URL可能有问题,比如:https://196.41.139.168:80/serve/?r=pdf/generatePdf 当Apache无法在该端口上与您进行https通信时,连接将被终止。
你意识到当你两行后将$body设置为数组时,你的第一个$body定义会被替换吗?{可能只是你试图解决问题的产物}你还没有对client_urlclient_id值进行编码(前者很可能包含需要转义的字符!)哦,而且你在没有初始化$body_str的情况下附加到它上面。
从你的详细输出中,我们可以看到cURL正在添加一个content-length头,但是...它是否正确?我在互联网上看到一些评论指出那个数字是错误的(特别是旧版本)...如果那个数字太小(例如),你会在发送所有数据之前收到连接重置。你可以手动插入头:
curl_setopt ($c, CURLOPT_HTTPHEADER, 
   array("Content-Length: ". strlen($body_str))); 

还有一个方便的函数http_build_query,可以将一组名称/值对的数组转换为URL编码的字符串。

所有这些都汇总成最终代码:

$post=http_build_query(array(
  "client_url"=>Yii::app()->params['pdfClientURL'],
  "client_id"=>Yii::app()->params['pdfClientID'],
  "title"=>$title,
  "content"=>$content));

//Open to URL
$c=curl_init(Yii::app()->params['pdfUrl']);
//Send post
curl_setopt ($c, CURLOPT_POST, true);
//Optional: [try with/without]
curl_setopt ($c, CURLOPT_HTTPHEADER, array("Content-Length: ".strlen($post))); 
curl_setopt ($c, CURLOPT_POSTFIELDS, $post);
curl_setopt ($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt ($c, CURLOPT_CONNECTTIMEOUT , 0);
curl_setopt ($c, CURLOPT_TIMEOUT  , 20);
//Collect result
$pdf = curl_exec ($c);
$curlInfo = curl_getinfo($c);
curl_close($c);

3
这是一个防火墙问题,如果您正在使用VMware应用程序,请确保杀毒软件的防火墙已关闭或允许连接。
如果此服务器位于安全网络上,请查看服务器的防火墙规则。
谢谢 Ganesh PNS

1
我们遇到了相同的问题,即在与负载均衡器建立websocket连接时出现问题。问题在于LB在端口80上接受http连接并将请求转发给节点(Tomcat应用程序在端口8080上)。我们已将其更改为在端口80上接受tcp(http已更改为“tcp”)连接。因此,第一个握手请求被转发到Node,并成功地在某个随机端口(据我所知)上建立了websocket连接。下面的命令用于测试websocket握手过程。
  • 重构的URL为: http:LB URL/
  • 尝试连接到LB URL...
  • TCP_NODELAY已设置
  • 已连接到LB URL (LB URL) 的80端口 (#0)

    GET / HTTP/1.1 Host: localhost User-Agent: curl/7.60.0 Accept: / Connection: Upgrade Upgrade: websocket Origin: http://LB URL:80

  • 接收失败: 连接被对等方重置
  • 关闭连接0 curl: (56) 接收失败: 连接被对等方重置

1
在我的情况下,URL 出现了问题。我使用了 https://example.com,但是它们确保了 'www.' - 因此当我切换到 https://www.example.com 时,一切都没问题了。正确的头信息被发送为 'Host: www.example.com'。
您可以尝试在 Firefox 浏览器中发出请求,将其持久化并复制为 cURL - 这就是我找到它的方式。

0
这可能是因为你使用curl请求创建的TCP连接在任务完成后没有立即关闭。这可能导致worker_connections(Nginx进程可持有的连接数)增加,并导致nginx服务器重置新连接。
我们可以通过在curl请求中添加--retry和--retry-all-errors标志(在最新版本的curl中可用)来避免此问题,以便在出现对等方连接重置的情况下进行curl重试。此外,增加nginx.conf文件中的worker_connections数量也可以帮助你避免达到TCP连接的最大限制。
尽量在curl请求完成后立即关闭所有打开的TCP连接。

0

0
在我的情况下,是本地虚拟机中的代理设置问题。请求被发送到不知道服务器的代理。通过绕过代理解决了这个问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接