#java #sockets #exception #tcp
#java #сокеты #исключение #tcp
Вопрос:
У меня есть клиент-серверное приложение, которое имеет 16 потоков (8 клиентских, 8 серверных), и у каждого есть TCP-соединение в соответствии 1-1 (таким образом, 8 потоков TCP).
У меня есть сервер, отправляющий X количество случайных байтов, а затем закрывающийся. Тем временем клиент просто считывает X объем данных, а затем закрывается. X предопределено. Я также использую dummynet для регулирования пропускной способности, но я оставляю канал с достаточной пропускной способностью (100 Мбит / с). Иногда это работает нормально, в других случаях я получаю эти исключения. Я переношу 1 ГБ, равномерно распределенный по всем 8 соединениям, примерно за 3,5 минуты.
Клиент выдает это исключение:
java.net.SocketException: Operation timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at miccbull.parralleltcp.client.StreamWorker.run(StreamWorker.java:43)
Сервер выдает это исключение:
java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
at java.net.SocketOutputStream.write(SocketOutputStream.java:124)
at miccbull.parralleltcp.server.TransmissionWorker.run(TransmissionWorker.java:51)
Серверный код:
public void run() {
OutputStream os = null;
try {
socket = sSocket.accept();
os = socket.getOutputStream();
} catch (IOException e2) {
e2.printStackTrace();
}
//send the data
for(int i = 0; i < Server.TRANSMISSIONS; i ){
System.out.println(sSocket.getLocalPort() " sending set " (i 1) " of " Server.TRANSMISSIONS);
try {
os.write(new byte[Server.FILE_SIZE/Server.numStreams/Server.TRANSMISSIONS]);
os.flush();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Closing server (at write bytes) with socket id: " sSocket.getLocalPort());
}
}
System.out.println("Worker " sSocket.getLocalPort() " done");
//close the socket
try {
socket.close();
sSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Клиентский код:
public void run(){
byte[] bytes = new byte[1024]; //number of bytes to read per client stream
InputStream is = null;
//connect to server
try {
connectionSocket = new Socket("localhost", id);
is = connectionSocket.getInputStream();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//read file from server
int value = 0;
int bytesRead = 0;
try {
while(bytesRead < (Client.FILE_SIZE/Client.NUM_STREAMS)){
value = is.read(bytes, 0, 1024);
if(value == -1){
System.out.println("******************************** Read is -1 for client " id);
}
else if(value == 0){
System.out.println("******************************** Read is 0 for client " id);
}
bytesRead = value;
}
} catch (IOException e) {
System.out.println("**** Exception in read in client " id " and value value was: " value);
if(bytesRead == (Client.FILE_SIZE/Client.NUM_STREAMS)){
System.out.println("************ NOT ACTUALLY BAD" id);
}
e.printStackTrace();
}
//Finished download
Client.workerDone();
System.out.println("Worker " id " done received " bytesRead);
Что вызывает эти исключения?
Ответ №1:
Ваш клиент никогда не прекращает чтение, потому что вы не проверяете, возвращает ли read() < 0.
Комментарии:
1. Привет, я прекращаю чтение, когда получаю все байты, которые я ожидаю от сервера, в частности, с этим условием
bytesRead < (Client.FILE_SIZE/Client.NUM_STREAMS)
. Тем не менее, я ввел туда несколько проверок значения, возвращаемого из read(), и оно никогда не было < 0 во время моего эксперимента. Что может быть причиной для беспокойства??2. Это становится очень неприятным. Спасибо за вашу помощь.
3. @Michael тем не менее, ваш код неверен. Что, если сервер отключится перед отправкой всех ожидаемых вами данных? Ваш код не обнаружит это и будет зацикливаться вечно.
Ответ №2:
Вот что я вижу в вашем коде:
-
Я проверил, что каждый вызов обоих
socket.getOutputStream()
иsocket.getInputStream()
возвращает другой поток, связанный с сокетом. Вы должны вызывать их каждый раз для каждого сокета и помещать результат в локальную переменную.OutputStream outputStream = socket.getOutputStream(); // send the data for (int i = 0; i < 10; i ) { .... outputStream.write(...);
-
Есть несколько мест, куда вы должны вернуться из своего
run()
метода, но я подозреваю, что вы это знали. -
Вы не проверяете возвращаемое значение из
read()
, чтобы увидеть, возвращает ли оно -1 при EOF. Вы не должны просто предполагать, что получаете правильное количество байтов. -
Я предполагаю, что вы пишете блоки из нулевых символов по какой-то причине.
Комментарии:
1. 1. «Я проверил, что каждый вызов как socket.getOutputStream(), так и socket.getInputStream() возвращает другой поток, связанный с сокетом». Я только что убедился в обратном из исходного кода. Так что, в конечном счете, это вообще не его проблема. 1.1. Очистка такого потока ничего не дает.
2. @Gray Привет, я думал, что потоки были одинаковыми, но я поместил их в локальные переменные, как вы предложили, и я проверил значения из read (). Я только что запустил тест и получил исключение BrokenPipe на клиенте с самым последним прочитанным значением 162. Я отправляю случайные байты, потому что это приложение предназначено только для тестирования производительности. Видите ли вы что-нибудь еще, что может вызывать подозрения?
3. @EJP, ха, у меня нет доступа к исходному коду JDK на моем Mac (grumble), но объекты имеют разные
toString()
значения, т.Е.Object.hashCode()
Так что это разные объекты, если я чего-то не упускаю. Я использую Sun JDK 1.6.0_26. Спасибо заread
исправление.4. @Michael, какие значения от read ты ожидаешь? Из-за буферизации и прерывания пакетных данных вы не будете получать полное чтение 1024 каждый раз.
read
не блокируется, пока не заполнится весь буфер. Если вы пытаетесь отправить «сущность» через, я бы рекомендовал использовать сериализацию и запись и чтение объекта. В противном случае вам придется самостоятельно обслуживать буфер с помощьюBufferedInputStream
и т.д..5. @Gray Меня устраивает любое положительное значение из
read()
, поскольку я просто добавляю это значение к общей сумме (value
), которая уже была прочитана (bytesRead
). Затем, когдаbytesRead
становится значением, которое я ожидаю от сервера,run
завершается. Я только что подтвердил, что клиент действительно выдает это исключение «Время ожидания операции истекло» до того, как он прочитал все данные с сервера. Пока спасибо за вашу помощь!