Mega Code Archive

 
Categories / Java / Network Protocol
 

A tiny version of Ward Christensens MODEM program for UNIX

/*  * Copyright (c) Ian F. Darwin, http://www.darwinsys.com/, 1996-2002.  * All rights reserved. Software written by Ian F. Darwin and others.  * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $  *  * Redistribution and use in source and binary forms, with or without  * modification, are permitted provided that the following conditions  * are met:  * 1. Redistributions of source code must retain the above copyright  *    notice, this list of conditions and the following disclaimer.  * 2. Redistributions in binary form must reproduce the above copyright  *    notice, this list of conditions and the following disclaimer in the  *    documentation and/or other materials provided with the distribution.  *  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  * POSSIBILITY OF SUCH DAMAGE.  *   * Java, the Duke mascot, and all variants of Sun's Java "steaming coffee  * cup" logo are trademarks of Sun Microsystems. Sun's, and James Gosling's,  * pioneering role in inventing and promulgating (and standardizing) the Java   * language and environment is gratefully acknowledged.  *   * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for  * inventing predecessor languages C and C++ is also gratefully acknowledged.  */ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; /**  * a tiny version of Ward Christensen's MODEM program for UNIX.   * Written ~ 1980 by Andrew Scott Beals. Last revised 1982.  * A.D. 2000 - dragged from the archives for use in Java Cookbook.  *  * @author C version by Andrew Scott Beals, sjobrg.andy%mit-oz@mit-mc.arpa.  * @author Java version by Ian F. Darwin, http://www.darwinsys.com/  * $Id: TModem.java,v 1.10 2004/04/11 23:50:40 ian Exp $  */ public class TModem {   protected final byte CPMEOF = 26;    /* control/z */   protected final int MAXERRORS = 10;    /* max times to retry one block */   protected final int SECSIZE  = 128;    /* cpm sector, transmission block */   protected final int SENTIMOUT = 30;    /* timeout time in send */   protected final int  SLEEP  = 30;    /* timeout time in recv */   /* Protocol characters used */   protected final byte  SOH  = 1;  /* Start Of Header */   protected final byte  EOT  = 4;  /* End Of Transmission */   protected final byte  ACK  = 6;  /* ACKnowlege */   protected final byte  NAK  = 0x15;  /* Negative AcKnowlege */   protected InputStream inStream;   protected OutputStream outStream;   protected PrintWriter errStream;   /** Construct a TModem */   public TModem(InputStream is, OutputStream os, PrintWriter errs) {     inStream = is;     outStream = os;     errStream = errs;   }   /** Construct a TModem with default files (stdin and stdout). */   public TModem() {     inStream = System.in;     outStream = System.out;     errStream = new PrintWriter(System.err);   }   /** A main program, for direct invocation. */   public static void main(String[] argv) throws      IOException, InterruptedException {     /* argc must == 2, i.e., `java TModem -s filename' */     if (argv.length != 2)        usage();     if (argv[0].charAt(0) != '-')       usage();     TModem tm = new TModem();     tm.setStandalone(true);     boolean OK = false;     switch (argv[0].charAt(1)){     case 'r':        OK = tm.receive(argv[1]);        break;     case 's':        OK = tm.send(argv[1]);        break;     default:        usage();     }     System.out.print(OK?"Done OK":"Failed");     System.exit(0);   }   /* give user minimal usage message */   protected static void usage()   {     System.err.println("usage: TModem -r/-s file");     // not errStream, not die(), since this is static.     System.exit(1);   }   /** If we're in a standalone app it is OK to System.exit() */   protected boolean standalone = false;   public void setStandalone(boolean is) {     standalone = is;   }   public boolean isStandalone() {     return standalone;   }   /** A flag used to communicate with inner class IOTimer */   protected boolean gotChar;   /** An inner class to provide a read timeout for alarms. */   class IOTimer extends Thread {     String message;     long milliseconds;     /** Construct an IO Timer */     IOTimer(long sec, String mesg) {       milliseconds = 1000 * sec;       message = mesg;     }          public void run() {         try {         Thread.sleep(milliseconds);       } catch (InterruptedException e) {         // can't happen       }       /** Implement the timer */       if (!gotChar)       errStream.println("Timed out waiting for " + message);       try {         die(1);       } catch (Exception e1) {         // TODO Auto-generated catch block         e1.printStackTrace();       }     }   }   /*    * send a file to the remote    */   public boolean send(String tfile) throws IOException, InterruptedException   {     char checksum, index, blocknumber, errorcount;     byte character;     byte[] sector = new byte[SECSIZE];     int nbytes;     DataInputStream foo;     foo = new DataInputStream(new FileInputStream(tfile));     errStream.println( "file open, ready to send");     errorcount = 0;     blocknumber = 1;     // The C version uses "alarm()", a UNIX-only system call,     // to detect if the read times out. Here we do detect it     // by using a Thread, the IOTimer class defined above.     gotChar = false;     new IOTimer(SENTIMOUT, "NAK to start send").start();     do {       character = getchar();       gotChar = true;       if (character != NAK && errorcount < MAXERRORS)         ++errorcount;     } while (character != NAK && errorcount < MAXERRORS);     errStream.println( "transmission beginning");     if (errorcount == MAXERRORS) {       xerror();     }     while ((nbytes=inStream.read(sector))!=0) {       if (nbytes<SECSIZE)         sector[nbytes]=CPMEOF;       errorcount = 0;       while (errorcount < MAXERRORS) {         errStream.println( "{" + blocknumber + "} ");         putchar(SOH);  /* here is our header */         putchar(blocknumber);  /* the block number */         putchar(~blocknumber);  /* & its complement */         checksum = 0;         for (index = 0; index < SECSIZE; index++) {           putchar(sector[index]);           checksum += sector[index];         }         putchar(checksum);  /* tell our checksum */         if (getchar() != ACK)           ++errorcount;         else           break;       }       if (errorcount == MAXERRORS)         xerror();       ++blocknumber;     }     boolean isAck = false;     while (!isAck) {       putchar(EOT);       isAck = getchar() == ACK;     }     errStream.println( "Transmission complete.");     return true;   }   /*    * receive a file from the remote    */   public boolean receive(String tfile) throws IOException, InterruptedException   {     char checksum, index, blocknumber, errorcount;     byte character;     byte[] sector = new byte[SECSIZE];     DataOutputStream foo;     foo = new DataOutputStream(new FileOutputStream(tfile));     System.out.println("you have " + SLEEP + " seconds...");     /* wait for the user or remote to get his act together */     gotChar = false;     new IOTimer(SLEEP, "receive from remote").start();      errStream.println("Starting receive...");     putchar(NAK);     errorcount = 0;     blocknumber = 1;     rxLoop:     do {        character = getchar();       gotChar = true;       if (character != EOT) {         try {           byte not_ch;           if (character != SOH) {             errStream.println( "Not SOH");             if (++errorcount < MAXERRORS)               continue rxLoop;             else               xerror();           }           character = getchar();           not_ch = (byte)(~getchar());           errStream.println( "[" +  character + "] ");           if (character != not_ch) {             errStream.println( "Blockcounts not ~");             ++errorcount;             continue rxLoop;           }           if (character != blocknumber) {             errStream.println( "Wrong blocknumber");             ++errorcount;             continue rxLoop;           }           checksum = 0;           for (index = 0; index < SECSIZE; index++) {             sector[index] = getchar();             checksum += sector[index];           }           if (checksum != getchar()) {             errStream.println( "Bad checksum");             errorcount++;             continue rxLoop;           }           putchar(ACK);           blocknumber++;           try {             foo.write(sector);           } catch (IOException e) {             errStream.println("write failed, blocknumber " + blocknumber);           }         } finally {         if (errorcount != 0)           putchar(NAK);       }     }     } while (character != EOT);     foo.close();     putchar(ACK);  /* tell the other end we accepted his EOT   */     putchar(ACK);     putchar(ACK);     errStream.println("Receive Completed.");     return true;   }   protected byte getchar() throws IOException {     return (byte)inStream.read();   }   protected void putchar(int c) throws IOException {     outStream.write(c);   }   protected void xerror()   {     errStream.println("too many errors...aborting");     try {       die(1);     } catch (Exception e) {       // TODO Auto-generated catch block       e.printStackTrace();     }   }   protected void die(int how) throws Exception   {     if (standalone)       System.exit(how);     else throw new Exception("Error code " + Integer.toString(how));   } }