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

标签 php apache http httpurlconnection socketexception

对位于我的 Godaddy 服务器上的 PHP 文件的一系列 60 个 HTTP POST 在第 61 次尝试时反复导致 SocketException(使用 Java JRE6/JRE7 时)、NoHttpResponseException(在 Android 中)或偶尔导致 SocketTimeoutException(Java 和 Android) ,并且服务器在大约 60 秒内变得无响应,此时我可以在发生相同异常之前偷偷处理更多请求。

java.net.SocketException:来自服务器的意外文件结尾 在 sun.net.www.http.HttpClient.parseHTTPHeader(未知来源) 在 sun.net.www.http.HttpClient.parseHTTP(未知来源) 在 sun.net.www.protocol.http.HttpURLConnection.getInputStream(未知来源) 在 Tester.postRequest(Tester.java:47) 在 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'];
?>

我花了几天时间查看这个问题,发现许多其他人发布了可追溯到几年前的类似类型的问题,但通常都没有得到解决。我尝试了以下方法,都出现了相同的失败。

  1. 我尝试了 Apache HttpClient(使用 DefaultHttpClient)和 HttpURLConnection。
  2. 我调整了请求的节奏,每个请求大约一秒,改进很小(例如,它在第 65 次迭代而不是第 61 次迭代时失败)
  3. 出现错误时,如果我等待 60 秒多一点(巧合?),我可以在另一个错误之前通过更多的 POST。
  4. 我试过 HttpUrlConnection 的所有参数。
  5. 我尝试了各种 HTTP header 设置。
  6. 我注释掉了与上面的 BufferedOutputStream 有关的代码,即。我做了一个空的 POST。
  7. 我修改了 PHP 文件以记录传入的请求。它看到了 60 个成功的请求,但从未收到第 61 个请求(失败的请求)。
  8. 把最好的留到最后。我在台式机(有线互联网)和笔记本电脑(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 数据库的 Android 应用程序。更烦人的是缺少文档,因为我花了一周时间调试上述问题。

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

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

相关文章:

php - 获取请求超时 PDO 连接

regex - 动态域 htaccess 重定向

php - Apache - 处理 TCP 连接,但不处理 HTTP 请求

ruby-on-rails - 使用 apache 和 passenger 部署 Rails 应用程序后,显示页面不存在

angular - 使用 typescript ,什么是最好的 Observable 类型来最好地表示没有内容的 HTTP 响应?

javascript - HTTP header 上的多个值

php - 是否有可能获得已定义 namespace 的列表

php - 泰米尔语未显示在 php 中

http - Dispatchers.IO 中有多少个线程?

php - 使用MWS获取商品信息时重新定义参数$quotaMax报错