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
* 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;
}
}
*