package Ymodem; import java.awt.Color; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; class Modem { /* Protocol characters used */ protected static final byte SOH = 0x01; /* Start Of Header */ protected static final byte STX = 0x02; /* Start Of Text (used like SOH but means 1024 block size) */ protected static final byte EOT = 0x04; /* End Of Transmission */ protected static final byte ACK = 0x06; /* ACKnowlege */ protected static final byte NAK = 0x15; /* Negative AcKnowlege */ protected static final byte CAN = 0x18; /* CANcel character */ protected static final byte CPMEOF = 0x1A; protected static final byte ST_C = 'C'; protected static final int MAXERRORS = 10; protected static final int BLOCK_TIMEOUT = 1000; protected static final int REQUEST_TIMEOUT = 3000; protected static final int WAIT_FOR_RECEIVER_TIMEOUT = 60_000; protected static final int SEND_BLOCK_TIMEOUT = 10_000; private final InputStream inputStream; private final OutputStream outputStream; private final byte[] shortBlockBuffer; private final byte[] longBlockBuffer; /** * Constructor * * @param inputStream stream for reading received data from other side * @param outputStream stream for writing data to other side */ protected Modem(InputStream inputStream, OutputStream outputStream) { this.inputStream = inputStream; this.outputStream = outputStream; shortBlockBuffer = new byte[128]; longBlockBuffer = new byte[1024]; } /** * Wait for receiver request for transmission * * @param timer * @return TRUE if receiver requested CRC-16 checksum, FALSE if 8bit checksum * @throws IOException */ protected boolean waitReceiverRequest(Timer timer) throws IOException { int character; while (true) { try { character = readByte(timer); if (character == NAK) return false; if (character == ST_C) { return true; } } catch (TimeoutException e) { } } } /** * Send a file.
*

* This method support correct thread interruption, when thread is interrupted "cancel of transmission" will be send. * So you can move long transmission to other thread and interrupt it according to your algorithm. * * @param file * @param useBlock1K * @throws IOException */ protected void send(Path file, boolean useBlock1K) throws IOException { //open file try (DataInputStream dataStream = new DataInputStream(Files.newInputStream(file))) { Timer timer = new Timer(WAIT_FOR_RECEIVER_TIMEOUT).start(); boolean useCRC16 = waitReceiverRequest(timer); CRC crc; if (useCRC16) crc = new CRC16(); else crc = new CRC8(); byte[] block; if (useBlock1K) block = new byte[1024]; else block = new byte[128]; sendDataBlocks(dataStream, 1, crc, block, dataStream.available()); sendEOT(); } } protected void sendDataBlocks(DataInputStream dataStream, int blockNumber, CRC crc, byte[] block, int zu) throws IOException { int dataLength; byte[] bytes = new byte[128]; int index = 0; int aaa = zu/100; int ccc = 0; while ((dataLength = dataStream.read(block)) != -1) { ccc += dataLength; if(dataLength >128 && dataLength <=896){ while (index> (8 * i)) & 0xFF); } outputStream.write(crcBytes); } /** * Receive file
*

* This method support correct thread interruption, when thread is interrupted "cancel of transmission" will be send. * So you can move long transmission to other thread and interrupt it according to your algorithm. * * @param file file path for storing * @throws IOException */ protected void receive(Path file, boolean useCRC16) throws IOException { try (DataOutputStream dataOutput = new DataOutputStream(Files.newOutputStream(file))) { int available; // clean input stream if ((available = inputStream.available()) > 0) { inputStream.skip(available); } int character = requestTransmissionStart(useCRC16); CRC crc; if (useCRC16) crc = new CRC16(); else crc = new CRC8(); processDataBlocks(crc, 1, character, dataOutput); } } protected void processDataBlocks(CRC crc, int blockNumber, int blockInitialCharacter, DataOutputStream dataOutput) throws IOException { // read blocks until EOT boolean result = false; boolean shortBlock; byte[] block; while (true) { int errorCount = 0; if (blockInitialCharacter == EOT) { // end of transmission sendByte(ACK); return; } //read and process block shortBlock = (blockInitialCharacter == SOH); try { block = readBlock(blockNumber, shortBlock, crc); dataOutput.write(block); blockNumber++; errorCount = 0; result = true; sendByte(ACK); } catch (TimeoutException | InvalidBlockException e) { errorCount++; if (errorCount == MAXERRORS) { interruptTransmission(); throw new IOException("Transmission aborted, error count exceeded max"); } sendByte(NAK); result = false; } catch (RepeatedBlockException e) { //thats ok, accept and wait for next block sendByte(ACK); } catch (SynchronizationLostException e) { //fatal transmission error interruptTransmission(); throw new IOException("Fatal transmission error", e); } //wait for next block blockInitialCharacter = readNextBlockStart(result); } } protected void sendByte(byte b) throws IOException { outputStream.write(b); outputStream.flush(); } /** * Request transmission start and return first byte of "first" block from sender (block 1 for XModem, block 0 for YModem) * * @param useCRC16 * @return * @throws IOException */ protected int requestTransmissionStart(boolean useCRC16) throws IOException { int character; int errorCount = 0; byte requestStartByte; if (!useCRC16) { requestStartByte = NAK; } else { requestStartByte = ST_C; } // wait for first block start Timer timer = new Timer(REQUEST_TIMEOUT); while (errorCount < MAXERRORS) { // request transmission start (will be repeated after 10 second timeout for 10 times) sendByte(requestStartByte); timer.start(); try { while (true) { character = readByte(timer); if (character == SOH || character == STX) { return character; } } } catch (TimeoutException ignored) { errorCount++; } } interruptTransmission(); throw new RuntimeException("Timeout, no data received from transmitter"); } protected int readNextBlockStart(boolean lastBlockResult) throws IOException { int character; int errorCount = 0; Timer timer = new Timer(BLOCK_TIMEOUT); while (true) { timer.start(); try { while (true) { character = readByte(timer); if (character == SOH || character == STX || character == EOT) { return character; } } } catch (TimeoutException ignored) { // repeat last block result and wait for next block one more time if (++errorCount < MAXERRORS) { sendByte(lastBlockResult ? ACK : NAK); } else { interruptTransmission(); throw new RuntimeException("Timeout, no data received from transmitter"); } } } } private void shortSleep() { try { Thread.sleep(10); } catch (InterruptedException e) { try { interruptTransmission(); } catch (IOException ignore) { } throw new RuntimeException("Transmission was interrupted", e); } } /** * send CAN to interrupt seance * * @throws IOException */ protected void interruptTransmission() throws IOException { sendByte(CAN); sendByte(CAN); } protected byte[] readBlock(int blockNumber, boolean shortBlock, CRC crc) throws IOException, TimeoutException, RepeatedBlockException, SynchronizationLostException, InvalidBlockException { byte[] block; Timer timer = new Timer(BLOCK_TIMEOUT).start(); if (shortBlock) { block = shortBlockBuffer; } else { block = longBlockBuffer; } byte character; character = readByte(timer); if (character == blockNumber - 1) { // this is repeating of last block, possible ACK lost throw new RepeatedBlockException(); } if (character != blockNumber) { // wrong block - fatal loss of synchronization throw new SynchronizationLostException(); } character = readByte(timer); if (character != ~blockNumber) { throw new InvalidBlockException(); } // data for (int i = 0; i < block.length; i++) { block[i] = readByte(timer); } while (true) { if (inputStream.available() >= crc.getCRCLength()) { if (crc.calcCRC(block) != readCRC(crc)) { throw new InvalidBlockException(); } break; } shortSleep(); if (timer.isExpired()) { throw new TimeoutException(); } } return block; } private long readCRC(CRC crc) throws IOException { long checkSumma = 0; for (int j = 0; j < crc.getCRCLength(); j++) { checkSumma = (checkSumma << 8) + inputStream.read(); } return checkSumma; } private byte readByte(Timer timer) throws IOException, TimeoutException { while (true) { if (inputStream.available() > 0) { int b = inputStream.read(); return (byte) b; } if (timer.isExpired()) { throw new TimeoutException(); } shortSleep(); } } class RepeatedBlockException extends Exception { /** * */ private static final long serialVersionUID = 1L; } class SynchronizationLostException extends Exception { /** * */ private static final long serialVersionUID = 1L; } class InvalidBlockException extends Exception { /** * */ private static final long serialVersionUID = 1L; } }