#java #sockets #synchronization #file-transfer
#java #сокеты #синхронизация #передача файлов
Вопрос:
Я пытался решить проблему многими способами, но безуспешно, и я также искал информацию на этом форуме, но с теми же результатами, так что поехали.
На самом деле я создаю серверный демон, который принимает клиентские запросы, а затем он (сервер) передает все файлы, содержащиеся в определенной папке. Я собираюсь опубликовать код sendFileData (на сервере) и receiveFileData (на клиенте).
Сервер использует:
public static void sendFileData(File file, Socket socket) throws FileNotFoundException, IOException, SocketException {
byte[] auxiliar = new byte[8192];
byte[] mybytearray = new byte[(int) file.length()];
int longitud = mybytearray.length;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
bis.read(mybytearray, 0, longitud);
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
int paquetes = longitud / 8187;
int resto = longitud % 8187;
int i = 0;
while(i<paquetes){//The length goes on the first 4 bytes and the 5th tells if there are more packets to send (8192 bytes or less).
byte[] bytes = ByteBuffer.allocate(4).putInt(8187).array();
auxiliar[0] = bytes[0];
auxiliar[1] = bytes[1];
auxiliar[2] = bytes[2];
auxiliar[3] = bytes[3];
auxiliar[4] = 1;
for(int j = 5; j < 8192; j ){
auxiliar[j] = mybytearray[i*8187 (j-5)];
}
os.write(auxiliar, 0, 8192);
i =1;
}
if(resto > 0){
byte[] bytes = ByteBuffer.allocate(4).putInt(resto).array();
auxiliar[0] = bytes[0];
auxiliar[1] = bytes[1];
auxiliar[2] = bytes[2];
auxiliar[3] = bytes[3];
auxiliar[4] = 0;
for(int j = 5; j < resto 5; j ){
auxiliar[j] = mybytearray[i*8187 (j-5)];
}
os.write(auxiliar, 0, resto 5);
}
os.flush();
}
И на стороне клиента:
public static void receiveFileData(String nombreFichero, Socket s) throws IOException{
File monitored = new File(nombreFichero);
if(monitored.exists() == false){
monitored.createNewFile();
}
byte[] mybytearray;
DataInputStream is = new DataInputStream(s.getInputStream());
FileOutputStream fos = new FileOutputStream(monitored);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int bytesRead = 0;
int hasNext = 1;
do {
bytesRead = is.readInt();//Leo longitud
try {
Thread.sleep(1);// HERE!!!!
} catch (InterruptedException e) {
}
// System.out.println("Bytes read " bytesRead );
if(bytesRead <= 8187 amp;amp; bytesRead > 0){
// System.out.println("Bytes leídos " bytesRead );
hasNext = is.readByte();//Leo si hay más datos por enviar
mybytearray = new byte[bytesRead];
is.read(mybytearray);
if(monitored.exists()){
synchronized(monitored){
bos.write(mybytearray, 0, mybytearray.length);
}
}
mybytearray = null;
}else{
System.out.println("Fuera de rango " bytesRead);
}
}while(hasNext == 1);
bos.close();
mybytearray = null;
System.out.println("Fichero recibido: " monitored.getAbsolutePath());
}
В коде receiveFileData, если я не помещаю Thread.sleep(1) или System.out.println() или что-то еще, что требует времени для выполнения, я не получаю данные на клиенте правильным образом, потому что readInt() возвращает очень большое число случайным образом, отрицательное или положительное (что подразумевает нехватку памяти и другие исключения).
Конечно, это что-то о синхронизации, но я думаю, что схема передачи между двумя методами правильная (возможно, клиент слишком медленный, а сервер слишком быстрый).
Что происходит?? Поскольку я не хочу помещать Thread.sleep , я думаю, что это не очень хорошее программирование.
Большое вам спасибо!
Комментарии:
1. Сколько разных потоков потенциально вызывают receiveFileData() ? Тот же вопрос для sendFileData()… Кроме того, если вы просто выполняете файл -> Передача файла… зачем вообще связываться с чем-либо, кроме byte[] ? Вам понадобится один int перед загрузкой данных, чтобы сообщить другой стороне, сколько байтов ожидать, но в остальном просто отправьте необработанные байты!
2. На сервере есть только один поток, который прослушивает новые соединения (accept()). Когда клиент пытается подключиться к серверу, сервер создает новый поток, который будет отдельно управлять потоками данных между клиентом и сервером, и после этого он продолжает прослушивать. Клиентская сторона имеет только один поток. Я думаю, что я пытался отправить полный буфер со всеми байтами файла, но я думаю, что это привело к сбою (я должен проверить, потому что меня сейчас нет дома). Спасибо за ваш ответ
3. И этот код передает весь файл, без изменений, из одного места в другое?
4. Я работаю с необработанными данными, но я использую readInt(), потому что это 4 байта, и я проверил это с помощью Wireshark, поэтому я не знаю:(
5. Да, это все. Я отправил файлы таким образом, потому что я не знаю тип файлов (они могут быть двоичными или текстовыми), поэтому я думаю, что отправка неизмененной копии файла — хороший способ сделать это.
Ответ №1:
is.read(байты) не гарантирует заполнения предоставленного массива байтов. Вам нужно проверить его возвращаемое значение, чтобы увидеть, сколько байтов было прочитано, или (лучше) использовать readFully().
Sleep(), вероятно, просто дает время для возврата всех байтов из сокета.
Комментарии:
1. О, большое вам спасибо, я проверю это в понедельник, и если все в порядке, я проверю ваш ответ как принятый!
2. Большое вам спасибо!! Готовое решение, но у меня также что-то не так с отправителем: когда длина файла % 8187 == 0, для последнего пакета hasNext = 1, и оно должно быть 0. Теперь все работает нормально!