#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
.