Вопрос

У меня проблема с Jakarta Commons HttpClient. Прежде чем мой самописный HTTPServer получит реальный запрос, есть один запрос, который полностью пуст.Это первая проблема. Первая проблема решена.Это было вызвано ненужным подключением URL! Вторая проблема заключается в том, что иногда данные запроса заканчиваются после третьей или четвертой строки http-запроса:

POST / HTTP/1.1
User-Agent: Jakarta Commons-HttpClient/3.1
Host: 127.0.0.1:4232

Для отладки я использую Axis TCPMonitor.Там все в порядке, кроме пустой просьбы.

Как я обрабатываю поток:

StringBuffer requestBuffer = new StringBuffer();

InputStreamReader is = new InputStreamReader(socket.getInputStream(), "UTF-8");

int byteIn = -1;
do {
    byteIn = is.read();
    if (byteIn > 0) {
        requestBuffer.append((char) byteIn);
    }
} while (byteIn != -1 && is.ready());

String requestData = requestBuffer.toString();

Найден новый способ обработки потока.Я читаю все параметры заголовка и использую 'content-length' для чтения данных публикации.

InputStream is = mySocket.getInputStream();
if (is == null) {
    return;
}
BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"));

// Read the request line
// ...
// ...

// Parse the header
Properties header = new Properties();
if (st.hasMoreTokens()) {
    String line = in.readLine();
    while (line != null && line.trim().length() > 0) {
        int p = line.indexOf(':');
        header.put(line.substring(0, p).trim().toLowerCase(), line.substring(p + 1).trim());
        line = in.readLine();
    }
}

// If the method is POST, there may be parameters
// in data section, too, read it:
String postLine = "";
if (method.equalsIgnoreCase("POST")) {
    long size = 0x7FFFFFFFFFFFFFFFl;
    String contentLength = header.getProperty("content-length");
    if (contentLength != null) {
        try {
            size = Integer.parseInt(contentLength);
        } catch (NumberFormatException ex) {
        }
    }
    postLine = "";
    char buf[] = new char[512];
    int read = in.read(buf);
    while (read >= 0 && size > 0 && !postLine.endsWith("\r\n")) {
        size -= read;
        postLine += String.valueOf(buf, 0, read);
        if (size > 0) {
            read = in.read(buf);
        }
    }
    postLine = postLine.trim();
    decodeParms(postLine, parms);
}

Как я отправляю запрос:

client.getParams().setSoTimeout(30000);

method = new PostMethod(url.getPath());
method.getParams().setContentCharset("utf-8");
method.setRequestHeader("Content-Type", "application/xml; charset=utf-8");
method.addRequestHeader("Connection", "close");
method.setFollowRedirects(false);

byte[] requestXml = getRequestXml();

method.setRequestEntity(new InputStreamRequestEntity(new ByteArrayInputStream(requestXml)));

client.executeMethod(method);

int statusCode = method.getStatusCode();

Есть ли у кого-нибудь из вас идея, как решить эти проблемы?

Алекс

Это было полезно?

Решение

Это может быть связано со вторым условием в вашем цикле while, метод isReady() может возвращать false, когда следующее чтение может быть заблокировано - но на самом деле вам все равно, блокируется оно или нет, поэтому мы можем просто удалить его (подробнее вы можете прочитать здесь: http://java.sun.com/j2se/1.5.0/docs/api/java/io/InputStreamReader.html#ready%28%29 ).Попробуйте перейти на это:

byte[] buf = new byte[500];
while((is.read(buf))>-1){
  requestBuffer.append(new String(buf).trim());
  buf = new byte[500];
}

Теперь вы должны получить весь запрос целиком.

Другие советы

Я не знаю о первой проблеме, но я думаю, что ваша вторая проблема связана с этим:

} while (byteIn != -1 && is.ready());

Если отправитель недостаточно быстро отправляет данные, получатель может вызвать is.ready() до того, как будет отправлен следующий пакет.Это приведет к is.ready() чтобы вернуться false что приведет к остановке цикла.

Минимальное исправление - изменить эту строку на:

} while (byteIn != -1);

Редактировать

Но на самом деле, вам нужно переписать метод в соответствии с ответом @simonlord .Это действительно плохая идея - читать небуферизованный поток по одному байту за раз.В конечном итоге вы выполняете системный вызов для каждого read звоните, что ужасно неэффективно.

ПРАВКА 2

Причина, по которой удаление is.ready() задержки вызваны тем, что вы не уделяли должного внимания протоколу HTTP.Проблема заключалась в том, что код HttpClient оставлял сторону запроса TCP-соединения открытой, чтобы разрешить повторное использование соединения.Простым (но неоптимальным) решением было бы настроить HttpClient для закрытия соединения на стороне запроса.Тогда ваш код сразу увидел бы EOF.То, что вы на самом деле сделали, было другим решением.

Честно говоря, вы даже не должны быть пытаясь для реализации протокола HTTP на стороне сервера, если вы не готовы глубоко понять всю спецификацию HTTP и добросовестно ее реализовать.Есть вероятность, что существующая реализация будет быстрее и надежнее, чем все, что вы можете создать вместе.Проблема с реализацией подмножества спецификации заключается в том, что вашему серверу может потребоваться взаимодействовать с реальным браузером, который использует части спецификации, которые вы не потрудились реализовать / протестировать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top