curl错误18 - 传输关闭,但存在未读取的数据

99

使用curl从URL检索数据时,有时(在80%的情况下)会出现错误18:传输关闭但仍存在未读数据。于是返回的一部分数据丢失了。奇怪的是,当CURLOPT_RETURNTRANSFER设置为false时,即curl_exec函数不返回数据而直接显示内容时,这种情况永远不会发生。

可能的问题是什么?我能设置某些选项以避免此类行为吗?


你能给我们提供你正在尝试的URL吗?如果你是在本地主机上测试,那么可能是连接不良的问题。 - Question Mark
3
你是否发送了 Connection: Close 头?如果是的话,尝试使用类似于 Connection: Keep-AliveKeep-Alive: *** 的内容,其中 *** 是您选择的有意义的数字(也许安全起见选择10秒;大多数现代浏览器使用300,即5分钟)。 - Dereleased
我在使用NodeJS express服务器并逐行流式传输结果时遇到了这个问题。对我来说,问题是在数据流输出后设置了响应头("Content-Type": "text/csv")。我的头部没有出现在响应中,似乎导致了CURL错误。我在流输出之前明确设置了标题,然后它开始工作了。 - Akron
14个回答

56

错误字符串就是libcurl所看到的内容:因为它正在接收块编码流,所以它知道何时还有数据要接收。当连接关闭时,libcurl知道上一个接收到的块不完整。然后你会得到这个错误代码。

如果请求未被修改,你无法避免此错误,但你可以尝试通过发出HTTP 1.0请求来解决此问题(因为那时不会发生块编码),但事实上,这很可能是服务器或你的网络/设置中的缺陷。


3
对于我来说,问题出在一个我无法控制的远程端,唯一有效的解决方法是使用以下代码强制使用1.0版本:curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - Eric Caron
@DanielStenberg 仍然存在同样的问题。 - user6067756
设置HTTP版本为1.0可以帮助我处理分块编码和在检索到的数据中出现的奇怪十六进制标记。非常感谢这个提示! - Givi
这是我们使用curl特定URL时发生的事情吗?@EricCaron - Mona Jalal
@MonaJalal 我没有运行远程服务器,所以不了解它的配置。我猜测IIS有不同的配置,并且对1.0和1.1请求有不同的响应。 - Eric Caron

42

我猜这可能与对等方发送的错误的Content-Length头有关。我的建议是让curl自己设置长度。


7
这可能与响应头中的 Content-Length 相关。我在同事的项目中遇到了类似情况:一个 Java WebService 网关将 Content-Length 设置为 601,而实际 XML 响应只有 210 个字节。 - pcdinh
我相信@pcdinh是正确的。我们遇到了未终止的分块传输编码问题。curl正在等待更多数据(服务器宣布要发送更多数据或者没有发送终止符0),但是服务器关闭了连接。 - iGEL
5
没有包含"Content-Length",附上示例。 - MariuszS
3
你们是如何修复这个问题的?我的代码中没有发送任何内容长度。是服务器自动添加了一些东西吗? - Mijoe
Content-Length 头也可以由服务器正确设置;但是可能会发生服务器截断响应的情况,因此它不会发送所有“承诺”的内容长度,这是由于生成/发送响应的脚本(例如 PHP 脚本)崩溃所致。请参见另一个问题的此响应:https://dev59.com/EOk6XIcBkEYKwwoYEP3x#76217574。 - nickshoe

17

在使用 Guzzle 的过程中也遇到了这个错误。以下的报文头对我有帮助:

看到这个错误

在使用 Guzzle 的过程中也遇到了这个错误。以下的报文头对我有帮助:

'headers' => [
    'accept-encoding' => 'gzip, deflate',
],

我使用Postman发出请求,得到了完整的响应和没有错误。然后我开始添加Postman发送到Guzzle请求的标头,这是修复问题的其中一个。


2
谢谢,这对我有用。我尝试使用Postman获取响应头,并发现响应返回gzip,所以我添加了这个。 - Parminder Singh
这很有帮助,但响应变成了gzip格式。 - papaRomik
这也解决了我的失眠问题,以及使用大型数据集查询 Lumen API 的问题。 - Renegade_Mtl

7

我曾经遇到过同样的问题,但是通过取消cURL发送的“Expect:100-continue”标头(以下是PHP代码,但应该与其他cURL API类似)成功解决了:

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

顺便提一下,我正在向JDK 6 REST中包含的HTTP服务器发送调用,但它存在各种问题。在这种情况下,它首先发送100响应,然后在某些请求中无法正确发送随后的200响应。


我们应该在哪里修改这个设置?当 cURL 在 Windows 中时,我不知道在哪里添加这行代码。 - DomainsFeatured
2
@DomainsFeatured 我不确定你所说的“cURL在Windows中”的意思,但在命令行中,您可以通过给它一个空值来抑制'Expect:'头:curl -H 'Expect:' ...希望这能帮到你... - jcsahnwaldt Reinstate Monica
我在IIS上运行WordPress,并且它使用cURL,所以我不知道在哪里指定此选项 :-/ - DomainsFeatured
我对WordPress不是很了解,但它是开源的,所以你应该能够找到相关的PHP代码部分,并基本上只需插入我的答案中的行(可能需要将$curl的名称更改为该代码中使用的名称)。 - jcsahnwaldt Reinstate Monica
我有点困惑,“Expect: 100”头部是用于服务器鼓励客户端发送数据,而不是用于客户端接收数据。那么如何抑制此头部可以帮助客户端接收数据呢?谢谢。 - Joey Sun

4

在生成响应期间,我的服务器进程遇到异常并且没有正常关闭连接,因此突然关闭了连接。curl仍然期望从连接获得数据并抱怨(无可厚非)。


4

遇到了类似的问题,我的服务器在nginx后面。 Web服务器(Python Flask)日志中没有错误,但是nginx日志中有一些错误消息。

[crit] 31054#31054: * 269464 open()“/var/cache/nginx/proxy_temp/3/45/0000000453”失败(13:权限被拒绝),同时读取upstream

我通过更正目录权限来解决这个问题:

/var/cache/nginx

我也遇到了这个问题。原因是将 nginx 作为用户ID daemon 运行,而不是 nginx -> nginx.conf:user nginx; ... 我的操作系统(Alpine)软件包安装假定您将以用户 nginx 而不是 daemon 运行 nginx。更改权限可能也可以解决问题,但在我的情况下,以 nginx 的身份运行更好。 - James Stevens

3

当我的服务器磁盘空间已满并在生成响应期间中途关闭连接时,我遇到了这个错误,并且只是关闭了连接。


2

我在使用pycurl时遇到了这个问题,我通过以下方式解决了它

c.setopt(pycurl.HTTP_VERSION, pycurl.CURL_HTTP_VERSION_1_0) 

就像Eric Caron所说的那样。


2
我已通过这种方式解决了这个错误。
$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, 'http://www.someurl/' );
curl_setopt ( $ch, CURLOPT_TIMEOUT, 30);
ob_start();
$response = curl_exec ( $ch );
$data = ob_get_clean();
if(curl_getinfo($ch, CURLINFO_HTTP_CODE) == 200 ) success;

错误仍然存在,但我可以将响应数据存储在变量中处理。

1
当我不小心将一个文件下载到自身时,出现了这个错误。(我在sshfs挂载的远程目录中创建了符号链接以使其可供下载,忘记切换工作目录并使用了-OJ)
我想当您看到这个信息时,它可能无法真正“帮助”您,因为这意味着您的文件已经损坏了。

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