Как мне прочитать ответ сервера, не блокируя меня?

#java

#java

Вопрос:

Я пишу прокси и у меня есть следующий код:

 Socket conUser;
Socket conDest;
try{
    ServerSocket ss = new ServerSocket(Integer.parseInt(p.getProperty("proxy.port")));
    while(true){
        //Connect to user
        conUser = ss.accept();
        BufferedReader inputFromUser = new BufferedReader(new InputStreamReader(conUser.getInputStream()));
        BufferedWriter outputToUser = new BufferedWriter(new OutputStreamWriter(conUser.getOutputStream(), "UTF8"));

        //Get user request
        StringBuffer req = new StringBuffer();
        getUserRequest(inputFromUser, req);
        System.out.println("User requested the following:");
        System.out.println(req);

        //Connect to server
        InetAddress a = InetAddress.getByName(determineHost(req));
        conDest = new Socket(a,80);

        //Send request to server
        BufferedWriter outputToServer = new BufferedWriter(new OutputStreamWriter(conDest.getOutputStream(), "UTF8"));
        InputStreamReader inputFromServer = new InputStreamReader(conDest.getInputStream(), "UTF8");
        outputToServer.write(req.toString());
        outputToServer.flush();

        System.out.println("==============================");
        System.out.println("Server replied with the following:");
        //Read reply from the server
        //=========================================
        int chars;
                while ((chars = inputFromServer.read()) != -1) {
                    System.out.print((char)chars);
                    outputToUser.write(chars);
                    outputToUser.flush();
                    //serverReply.append(chars);
              }
               //Relay reply to user
             //outputToUser.write(serverReply.toString());
             //System.out.println(serverReply);
             //outputToUser.flush();
        conUser.close();
        conDest.close();
    }
} 
catch (Exception e) {
      System.err.println(e); 
}
  

Что происходит: я устанавливаю соединение, и оно выполняется успешно. Я также отправляю запрос, и это тоже успешно. Я также получаю ответ и могу загрузить HTML-код всей страницы, за исключением того, что чтение, похоже, не завершается, когда оно достигает конца содержимого.

В частности, я пытался загрузить домашнюю страницу Google, и фрагментированная передача достигла 0 (то есть — конец фрагментированной передачи), и, следовательно, больше не должно было быть ввода для чтения, но это не привело к прекращению чтения цикла. Что также странно для меня, так это то, что практически все примеры кода прокси используют этот цикл, и, предполагая, что они работают, я не вижу особых различий между их кодом и моим.

Как мне корректно завершить цикл?

РЕДАКТИРОВАТЬ: для протокола, да — я знаю, что TCP-соединение должно оставаться открытым для обработки дальнейших подключений. Это не имеет отношения к возникшей у меня проблеме. Мне нужно, чтобы этот цикл завершался для каждого ответа.

Комментарии:

1. Я полагаю, сервер думает, что вы, возможно, захотите сделать другой запрос в том же сокете. Вы должны либо сообщить ему, что не хотите этого делать, либо отключиться, когда у вас будет достаточно данных (он скажет вам, сколько ожидать)

2. Что произойдет, если вы используете > 0 вместо != -1 ? Я не уверен, сработает ли это с read(), но это должно работать с read(char[], int, int), потому что в последнем случае вы запрашиваете определенное количество символов или все остальные, если доступно меньше. Ноль подразумевает, что их больше нет — что не совсем то же самое, что «конец потока», но, похоже, работает лучше для меня.

Ответ №1:

В общем случае соединение не закрывается в конце каждого ответа. Создание TCP-соединений занимает относительно много времени, поэтому соединение остается открытым, готовым для отправки вашего следующего запроса.

Вот пара пояснительных ссылок:

Комментарии:

1. Да, я знаю о постоянных соединениях и конвейерной обработке, но это не отвечает на мой вопрос. Я хочу, чтобы соединение завершалось для каждого ответа.

2. Если вы хотите прервать соединение, тогда все усложняется, поскольку вашей программе придется больше понимать протокол HTTP. Я вижу два варианта. 1) Добавьте заголовок «Соединение: закрыть» к запросу, который вы передаете серверу. 2) Завершите цикл обработки ответа, когда вы прочитаете полный ответ (либо прочитайте байты длины содержимого, либо фрагментов больше нет). В таком случае вы действительно пишете полноценный HTTP-прокси, что намного сложнее, чем «echoer» сокета. совет axtavt рассмотреть существующие библиотеки звучит неплохо.

Ответ №2:

Если вы хотите правильно завершить соединение после получения HTTP-ответа, вашего простого цикла недостаточно. Вы должны определить конец сообщения, как описано в разделе 4.4 Длина сообщения RFC 2616, а затем закрыть соединение.

Однако было бы лучше использовать существующие библиотеки, такие как встроенные URLConnection .