php - 从 Java 到 PHP 文件执行许多(顺序)HTTP POST 命令时出现服务器异常

标签 php apache http httpurlconnection socketexception

对位于我 Godaddy 服务器上的 PHP 文件的一系列 60 个 HTTP POST 在第 61 次尝试时反复导致 SocketException(使用 Java JRE6/JRE7 时)、NoHttpResponseException(在 Android 中)或偶尔出现 SocketTimeoutException(Java 和 Android),并且服务器变得无响应大约 60 秒,此时我可以在相同的异常发生之前偷偷通过几个请求。
java.net.SocketException: Unexpected end of file from server at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source) at sun.net.www.http.HttpClient.parseHTTP(Unknown Source) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source) at Tester.postRequest(Tester.java:47) at Tester.main(Tester.java:20)
在花了一周的时间试图从我的 Android 项目中找出原因后,我最终将其简化为如下所示的自包含独立 Java 测试用例。在这种情况下,循环正确执行了 60 次(我解析响应并看到 PHP 文件看到了 POST),然后在第 61 次尝试时失败。这是非常可重复的,尽管我可以通过在 PHP 文件中添加代码(例如记录到文件等)来向下调整数字。请注意,执行 HTTP GET 不会导致问题,只会导致 POST。

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class Tester
{
    private static final String SERVER_PHP_URI="http://allucanapp.com/phptest.php";
    private static final String CHARSET="UTF-8";

    public static void main(String[] args)
    {
        try
        {
            for (int i=0; i < 200; i++)
                postRequest(i);
        } catch (IOException e) { e.printStackTrace(); }
    }

    private static void postRequest(int count) throws IOException
    {
        String query="command="+count;

        HttpURLConnection conn=(HttpURLConnection) (new URL(SERVER_PHP_URI).openConnection());

        try
        {
            conn.setReadTimeout(10000);
            conn.setConnectTimeout(15000);
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            conn.setFixedLengthStreamingMode(query.length());

            BufferedOutputStream output = null;
            try
            {
                output = new BufferedOutputStream(conn.getOutputStream());
                output.write(query.getBytes(CHARSET));
                output.flush();
            } finally { if (output != null) try { output.close(); } catch (IOException e) { e.printStackTrace(); } }

            InputStream response = conn.getInputStream();

            if (conn.getResponseCode() != HttpURLConnection.HTTP_OK)
                System.out.println("Error "+conn.getResponseCode()+": "+conn.getResponseMessage());
            else
            {
                BufferedReader reader = null;
                try
                {
                    reader = new BufferedReader(new InputStreamReader(response,CHARSET));
                    for (String line; (line = reader.readLine()) != null;)
                        System.out.println(count+":"+line+"\n");
                }
                finally { if (reader != null) try { reader.close(); } catch (IOException e) { e.printStackTrace(); } }
                response.close();
            }
        }
        finally { conn.disconnect(); }
    }
}

作为引用,这里是 PHP 文件。服务器支持 PHP 5.3 版。
<?php
echo "hi there " . $_POST['command'];
?>

我花了几天时间研究这个问题,发现许多其他人在几年前发布了类似类型的问题,但通常都没有得到解决。我试过以下,都表现出同样的失败。
  • 我尝试了 Apache HttpClient(使用 DefaultHttpClient)和 HttpURLConnection。
  • 我调整了请求的速度,每个请求大约一秒钟,但改进很小(例如,它在第 65 次迭代而不是第 61 次迭代失败)
  • 发生错误时,如果我等待超过 60 秒(巧合?),我可以在另一个错误之前获得更多的 POST。
  • 我使用了 HttpUrlConnection 的所有参数。
  • 我使用了所有各种 HTTP header 设置。
  • 我注释掉了与上面的 BufferedOutputStream 相关的代码,即。我做了一个空的POST。
  • 我修改了 PHP 文件以记录传入的请求。它看到了 60 个成功的请求,但从未收到第 61 个请求(失败的请求)。
  • 把好东西留到最后。我在我的台式机(有线互联网)和笔记本电脑(wifi 连接到同一个路由器)上运行了这段代码。
  • a) 如果我在一台机器上运行,它会在 60 次迭代后失败,然后如果我在另一台机器上运行,它会立即失败。
  • b)如果我同时运行两者,都运行约 30 次迭代,当一个失败时,另一个立即失败。
  • c) 如果我将笔记本电脑切换为在 3G 上运行,因此它不共享路由器,而 (a) 仍然发生,(b) 没有发生,它们由服务器独立处理。

  • 使用我的本地 (10.0.2.2) Zend 服务器时,此问题不会出现。如果我不知道更好,我会认为 Godaddy 服务器正在节流,也许是某种反拒绝服务策略。我会联系 Godaddy,但除了限制并发请求外,我找不到任何控制这种行为的 php.ini 设置。令我困扰的是,这个问题已经在我的代码库中潜伏了 2.5 个月,并且只是因为我在进行性能测试才表现出来。

    为了避免关于我为什么要重复 POST 等的评论,请记住上面是一个测试用例,原始应用程序使用 POST 将图像文件上传到服务器,虽然我可以合并这些以减少请求的数量,这只是一个杂项并产生新的问题(文件上传大小的限制等)

    最佳答案

    事实证明,我的测试用例被他们的服务器标记为 DDoS 事件,这会暂时将我客户的 IP 地址列入黑名单。他们声称一分钟内的 6 次尝试被视为威胁。我想我很“幸运”,我可以在一分钟内完成 60 个请求。他们的解决方案?升级到他们的虚拟专用服务器每月花费大约 6 倍,他们认为这个限制不存在,因为我将负责保护服务器。不完全是最有帮助的回应。我猜他们的安全解决方案是迎合点击网络表单的人,而不是使用 PHP 与 MySQL 数据库接口(interface)的 Android 应用程序。更烦人的是缺少文档,因为我花了一周时间调试上述问题。

    无论如何,相反,我将尝试将我所有的 HTTP POST 请求组合成一个非常小的数字的笨拙路线,例如,一个用于上传一百张图像的超长多部分请求。然后弄清楚如何处理跟踪中间上传/下载进度,避免缓冲区溢出,固有的 PHP 限制等,并希望我不会遇到其他 Godaddy 服务器限制(例如 PHP 脚本超时!)。

    关于php - 从 Java 到 PHP 文件执行许多(顺序)HTTP POST 命令时出现服务器异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18896934/

    相关文章:

    SoapHeader 子节点中的 PHP 命名空间

    apache - htaccess 拒绝所有获取 apache 服务器测试页面

    php - 使用MAMP将Django/mod_wsgi和PHP作为同一Apache服务器上的虚拟主机

    ios - xcode 的 Http Post 示例

    php - 如何在Heroku上访问CakePHP日志文件?

    php - 返回javascript代码时如何在PHP/Dojo中正确生成异常

    android - http删除403错误tomcat

    http - 浏览器如何存储/重新创建授权 token ?

    php - 如何从本地数据库(mysql)到android获取上传的图像?

    apache - Mod_Jk 负载均衡