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

时间:2013-09-19 14:06:09

标签: php apache http httpurlconnection socketexception

对位于我的Godaddy服务器上的PHP文件的一系列60个HTTP POST反复导致SocketException(当使用Java JRE6 / JRE7时),NoHttpResponseException(在Android中)或偶尔会在第61个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'];
?>

我花了好几天时间看这个,发现很多其他人发布了几年前类似的问题,一般都没有解决。我尝试过以下几种,都表现出同样的失败。

  1. 我尝试了Apache HttpClient(使用DefaultHttpClient)和HttpURLConnection。
  2. 我节奏了这些请求,大约第二个请求只有很小的改进(例如,它在第65次迭代而不是第61次失败)
  3. 一旦出现错误,如果我等待超过60秒(巧合?),我可以在另一次错误之前再接种一些POST。
  4. 我玩了HttpUrlConnection的所有参数。
  5. 我使用了各种HTTP标头设置。
  6. 我注释掉了与上面的BufferedOutputStream相关的代码,即。我做了一个空的POST。
  7. 我修改了PHP文件以记录传入的请求。它看到了60个成功的,但从未收到第61个请求(失败的请求)。
  8. 为最后一次保存最好的。我在我的桌面(有线互联网)和笔记本电脑(连接到同一路由器的wifi)上运行此代码。
    • a)如果我在一个上运行,它会在60次迭代后失败,然后如果我在另一台机器上运行,它会立即失败。
    • b)如果我同时运行两次,都会运行~30次迭代,当一次失败时,另一次会立即失败。
    • c)如果我将笔记本电脑切换为在3G上运行,那么它不会共享路由器,而(a)仍然会发生,(b)不会,它们会被服务器独立处理。
  9. 使用本地(10.0.2.2)Zend服务器时,不会出现此问题。如果我不知道更好,我会认为Godaddy服务器是限制的,也许是一些反拒绝服务的策略。我会联系Godaddy,但除了限制并发请求之外,我找不到任何控制此类行为的php.ini设置。令我感到困扰的是,这个问题一直潜伏在我的代码库中2.5个月,而且只是因为我正在进行性能测试而表现出来。

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

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

听起来好像他们在一定数量的请求/时间之后限制了你的请求,这可能就像你提到他们防止针对他们的服务器的潜在DDoS攻击,或者他们没有分配一个服务请求的客户端线程数非常高。

如果有少量线程可用于处理您的请求并且他们通过您的努力不断连接,那么他们将积压并最终超时导致您的响应不足。

精彩的帖子有很多细节,很高兴看到你的尝试,我建议联系GoDaddy,因为我很确定这是他们的限制。

让我知道它是如何实现的,祝你好运