It is currently Mon Jul 23, 2018 8:01 am

All times are UTC + 1 hour [ DST ]




Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: Wed Mar 26, 2008 6:26 am 
Offline

Joined: Wed Mar 26, 2008 6:04 am
Posts: 4
Thanks to Fred Potter, I have the barebones framework for developing a Java TCPXmodem Class library for use to implement legacy file transfer support in my CBBS server project.

http://www.retrogradebbs.com

So far, I have been able to finish the alpha version of the Xmodem Receive routine, which only supports ASCII text file uploads currently for some unknown reason (binary file uploads bomb on the first block being received), but works well enough for demonstration. I am writing the code for my file library subsystem, which calls the methods in the Java class to actually process the file transfer.

Fred never coded a send routine for his Java Xmodem implementation, so I do not have one yet.

Rather than reinventing the wheel... or developing a flawed one based on a buggy algorithm from another developer (Fred), I wanted to see if anyone is willing to look at the following code and help me refine or rewrite whatever sections need to be modified...

I never thought I would get this far with this project myself, by myself, after Fred gave me his old code when he washed his hands of the endeavor, but I did and I want to wrap it up and finish the file transfer subsystem module for CBBS so callers can upload and download files.

Here is the link to the project: (contained in the .zip file) that you can download, compile, and test... it is self explanatory... its basically a helloworldish server application that starts receiving a file being sent via Xmodem after a caller is connected. After the file is sent and saved, the caller is disconnected. This worked well for testing and development of the actual Xmodem protocol code. I am simply passing the IO stream object from the socket instead of a RS-232 IO stream object as Fred intended.

Here is the code -- see if you can find the bugs in the design or in the code implementation...

import java.net.*;
import java.lang.*;
import java.io.*;

// X-Modem Server implementation via TCP/IP socket

public class XModemFileXfer {

public static FileOutputStream fw;
public InputStream inputStream;
public OutputStream outputStream;

public XModemFileXfer() {

System.out.println("Xmodem Ready!");
}

public boolean receiveFile(Socket socket, String fileName) {

// initialize the socket I/O streams

try {

inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
} catch (Exception e) {
System.out.println("Xmodem socket error!");
System.exit(0);
}

// open file

try {

fw = new FileOutputStream(fileName);

} catch (Exception e) {
System.out.println("File I/O error: cannot create file!");
System.exit(0);
}

InputStream incomingFile;

try {
incomingFile = new XModemRXStream(inputStream, outputStream);
} catch (IOException e) {
System.out.println("ERROR! Unable to start file transfer!");
e.printStackTrace();
return false;
}

System.out.println("Starting file transfer...");

/**
* Here we are reading from the incoming file, byte by byte, and printing out.
*
* Behind The Scenes: Internally, the read() method is handling the task of
* asking for the next data block from the remote computer, processing it (i.e.
* parsing, running checksums), and then putting it in an internal buffer. Not
* all calls to read() will request a new data block as each block contains at
* least 128 bytes of data. Sometimes you will only hit the buffer.
*/
try {
for (;;) {
int c = incomingFile.read();

if (c==-1)
break; // End of File

// print character / byte
//System.out.print(c+",");

// write to file
try {

//System.out.print("."); // debug to count 128 bytes per block sent

fw.write(c);

} catch (Exception e) {
System.out.println(e);
System.exit(0);
}

}
} catch (IOException e) {
System.out.println("error while reading the incoming file.");
e.printStackTrace();
return false;
}

// done

System.out.println("File sent.");
new PrintStream(outputStream).println("");
new PrintStream(outputStream).println("transfer successful!");

// close file

try {

fw.close();
System.out.println("file saved.");

} catch (Exception e) {
System.out.println(e);
System.exit(0);
}

return true; // transfer was good

} // method

} // class



/**
* XModemRXStream is an easy to use class for receiving files
* via the XModem protocol.
*
* @author Fred Potter & Dave Hartman
* @version 0.2
*/

class XModemRXStream extends InputStream {

// data streams
private InputStream in;
private OutputStream out;

// constants
private static final int SOH = 0x01;
private static final int EOT = 0x04;
private static final int ACK = 0x06;
private static final int NAK = 0x15;
private static final int CAN = 0x18;
private static final int CR = 0x0d;
private static final int LF = 0x0a;
private static final int EOF = 0x1a;

// block size
private static final int bs = 128;

private int ebn; // expected incoming block #
private byte[] data; // our data buffer
private int dataPos; // our position with the data buffer

private int goodBlocks = 0;
private int badBlocks = 0;

/**
* Creates a new InputStream allowing you to read the incoming file. All of the XModem
* protocol functions are handled transparently.
*
* As soon as this class is instantiated, it will attempt to iniatate the transfer
* with the remote computer - if unsuccessful, an IOException will be thrown. If it
* is successful, the file transfer will begin.
*
* NOTE: It is important not to wait too long in between calls to read() - the remote
* computer will resend a data block if too much time has passed or even just give up
* on the transfer altogether.
*
* @param in InputStream from Serial Line
* @param out OutputStream from Serial Line
*/

public XModemRXStream(InputStream in, OutputStream out) throws
IOException {

this.in = in;
this.out = out;

//
// Initiate the receive sequence - send a NAK until the data
// starts flowing.
//

init:for (int t = 0; t < 10; t++) {

System.out.println("Waiting for upload to start [ try #" + t + " ]");

long mark = System.currentTimeMillis();

out.write(NAK);

// Frequently check to see if the data is flowing, give up after a couple seconds.

for (; ; ) {
if (in.available() > 0) {
break init;
}
try {
Thread.sleep(10);
}
catch (Exception e) {}
if (System.currentTimeMillis() - mark > 2000) {
break;
}
}

}

// We have either successfully negotiated the transfer, OR, it was
// a failure and timed out. Check in.available() to see if we have incoming
// bytes and that will be our sign.

if (in.available() == 0) {
throw new IOException();
}

//
// Initialize some stuff
//

ebn = 1; // the first block we see should be #1
data = new byte[bs];
dataPos = bs;

}

/**
* Reads the next block of data from the remote computer.
* Most of the real XModem protocol
* is encapsulated within this method.
* @throws IOException
*/

private synchronized void getNextBlock() throws IOException {


//System.out.println("Getting block #" + ebn);


//
// Read block into buffer. There is a 1 sec timeout for each character,
// otherwise we NAK and start over.
//

byte[] buffer;

recv:for (; ; ) {
buffer = new byte[bs + 4];
for (int t = 0; t < 10; t++) {
System.out.println("\nBlock [" + ebn + "] [Good [" + goodBlocks + "] Bad [" + badBlocks + "]");
// Read in block
buffer = new byte[buffer.length];
for (int i = 0; i < buffer.length; i++) {
int b = readTimed(1);

// if EOT - don't worry about the rest of the block.
if ( (i == 0) && (b == EOT)) {
buffer[0] = (byte) (b & 0xff);
break;
}

/////////////////////////////////////////////////////////////// BUG
// credits(rCurtis) block #24 is sending the CAN command (ASCII 24)
///////////////////////////////////////////////////////////////

// if CAN - the other side has cancelled the transfer
if (b == CAN) {
//throw new IOException("cancelled");
}

if (b < 0) {

System.out.println("Time out... NAK'ing");

out.write(NAK);

continue recv;
}
else {
buffer[i] = (byte) (b & 0xFF);
}

}
break;
}

int type = buffer[0] & 0xff; // either SOH or EOT

if (type == EOT) {

System.out.println("EOT!");

out.write(ACK);
break;
}

int bn = buffer[1] & 0xff; // block number
int bnc = buffer[2] & 0xff; // one's complement to block #

if ((bn != ebn) && (bn != (ebn - 1)) || (bn + bnc != 255)) {

System.out.println("NAK'ing type = " + type + " bn = " + bn +
" ebn = " +
ebn + " bnc = " + bnc);
out.write(NAK);

// increment bad blocks
badBlocks++;

continue recv;
}

byte chksum = buffer[ (buffer.length - 1)];

byte echksum = 0;
for (int i = 3; i < (buffer.length - 1); i++) {
echksum = (byte) ( ( (echksum & 0xff) + (buffer[i] & 0xff)) & 0xff);

}
if (chksum != echksum) {
out.write(NAK);
continue recv;
}

out.write(ACK);

// increment good blocks
goodBlocks++;

if (ebn == 255) {
ebn = 0;
}
else {
ebn++;

}

break;

}

// We got our block, now save it in our data buffer.
data = new byte[bs];
for (int i = 3; i < (buffer.length - 1); i++) {
data[(i - 3)] = buffer[i];
}

dataPos = 0;

}

public synchronized int read() throws IOException {


// If at the end of our buffer, refill it.
if (dataPos == bs) {
try {
getNextBlock();
}
catch (IOException e) {
throw new IOException();
}
}

// If we're still at end of buffer, say so.
if ( dataPos == bs) {
return -1;
}

int d = data[dataPos];

if (d == EOF)
return -1;

dataPos++;

return d;

}

/**
* A wrapper around the native read() call that provides the ability
* to timeout if no data is available within the specified timeout value.
* @param timeout timeout value in seconds
* @throws IOException
* @return int an integer representing the byte value read.
*/

private int readTimed(int timeout) throws IOException {
long start = System.currentTimeMillis();

for (; ; ) {
if (in.available() > 0) {
return (in.read());
}
try {
Thread.sleep(10);
}
catch (InterruptedException ex) {
}
//if (System.currentTimeMillis() - start > timeout * 1000) {
if (System.currentTimeMillis() - start > timeout * 5000) {
return -1;
}
}
}

}

I am confident that there are people on this forum that can easily figure this out, i.e. brain, RaveGuru, ShadowM, etc.

Help me put this baby to rest. I contacted WardXmodem (AOL) on IM and he was not able to follow what I am trying to do because he is not a proficient TCP client-server Java programmer.

I will give mega-props in the credits for anyone or everyone who assists me with this project for CBBS.

s0urc3r0r


http://www.retrogradebbs.com/projects/xmodemtcp/


Attachments:
File comment: the source code
xmodemviatcp.zip [160.4 KiB]
Downloaded 512 times
Top
 Profile  
Reply with quote  
 Post subject: C# Xmodem Implementation
PostPosted: Fri Mar 28, 2008 3:09 am 
Offline

Joined: Wed Mar 26, 2008 6:04 am
Posts: 4
I have a working TCP xmodem file server which supports downloads just fine but hangs on uploads. The transfer never starts for some reason... any ideas people?

Dave


using System;
//using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Threading;

namespace XModem
{
////////////////////////////////////
// Created by Andreas Wögerbauer //
// http://net.trackday.cc //
////////////////////////////////////

//This class is tested to by 100% compatibel to Microsoft(r) Hyperterminal 5.1

//XmodemTransmit: supports both CRC and checksum
//XModemReceive: only supports checksum

//Feel free to use/modify/copy/distribute this file
public class XModem
{
#region Events

public event System.EventHandler PaketReceived;
public event System.EventHandler PaketSent;

#endregion

#region Members

// create I/O streams for the client connection

System.Net.Sockets.TcpClient client = null;
System.IO.Stream inputStream = null;
System.IO.Stream outputStream = null;

private byte SOH = 0x01; //header for 128byte-pakets
private byte STX = 0x02; //header for 1024byte-pakets
private byte EOT = 0x04; //end of transmission
private byte ACK = 0x06; //acknowledge
private byte NAK = 0x15; //negativ acknowledge
private byte CAN = 0x18; //cancel transfer
private byte CTRLZ = 0x1A; //padding char to fill data blocks < buffer size
private byte XM_NAK = 0x15;

private ushort MAXRETRANS = 25;

//1024 for XModem 1k + 3 head chars + 2 crc + nul
private byte[] xbuff = new byte[1030];

private int bufsz, crc = -1;
private byte packetno = 1;
private int i, c, len = 0;
private int retry;

#endregion

#region Constructor

public XModem(System.Net.Sockets.TcpClient client)
{
this.client = client;
inputStream = (System.IO.Stream) client.GetStream();
outputStream = (System.IO.Stream) client.GetStream();

System.Console.WriteLine("IO streams initialized");
}

#endregion

#region Implementation

#region XModemReceive

///////////////////////////
//receive byte Array via XModem using Checksum error detection
/// ///////////////////////
public byte[] XModemReceive()
{
//since we don't know how many bytes we receive it's the
//easiest solution to use an arraylist as buffer and finally
//convert the arraylist to a byte[]

System.Console.WriteLine("ready to receive data using checksum mode");

ArrayList buf = new ArrayList();
int packetno = 1;
int retry = 0;
int i, c;
double kb = 0;

//send a NAK to notify client ready to receive (not using CRC)

this.writeByte(XM_NAK);

while (retry < 16)
{
retry++;

try
{
System.Console.WriteLine("retry #" + retry);
c = inputStream.ReadByte();
Console.Write(c);
}
catch (Exception e)
{
c = 0x00;
Console.WriteLine("c = 0x00");
}

if (c == SOH)
{
retry = 0;
bufsz = 128;

#region fill packet with datastream

xbuff[0] = (byte)c;
for (i = 0; i < (bufsz + 3); ++i)
{
xbuff[i + 1] = (byte)this.inputStream.ReadByte();
}

#endregion

if (xbuff[1] == (byte)(~xbuff[2]) && xbuff[1] == packetno && check(xbuff, 3, bufsz))
{
#region add buffer to arraylist

for (i = 0; i < (bufsz + 3); ++i)
{
//skip first 3 byte
if (i >= 3)
buf.Add(xbuff[i]);
}//for

#endregion

this.writeByte(ACK);

System.Console.Write(".");

//128 Byte per package
double d = 128;
d = d / 1024;
kb = (kb + d);

if (this.PaketReceived != null)
PaketReceived(this, null);

packetno++;
if (packetno >= 256)
packetno = 0;
}
}//if SOH
else if (c == EOT)
{
this.flushinput();
this.writeByte(ACK);

//create byte[]
byte[] byte_buffer = new byte[buf.Count];
int index = 0;
foreach (byte b in buf)
{
byte_buffer[index] = b;
index++;
}
return byte_buffer;
}//if EOT
else if (c == CAN)
{
if (inputStream.ReadByte() == CAN)
{
this.flushinput();
this.writeByte(ACK);

System.Console.WriteLine("cancelled by remote");

return null; //canceled by remote
}
}//if CAN
}//while()

//timeout
this.writeByte(CAN);
this.writeByte(CAN);
this.writeByte(CAN);
this.flushinput();

return null;
}

private bool check(byte[] buf, int index, int sz)
{
int i;
byte cks = 0;
for (i = 0; i < sz; ++i)
{
cks += buf[i + index];
}
if (cks == buf[sz + index])
return true;
return false;
}

#endregion

#region XmodemTransmit

public int XmodemTransmit(byte[] src, int srcsz)
{

System.Console.WriteLine("sending data...");

for (retry = 0; retry < 16; ++retry)
{
c = inputStream.ReadByte();
if (c >= 0)
{
if (c == 'C')
{
crc = 1;
System.Console.WriteLine("CRC mode detected");

return this.startTrans(src, srcsz);
}
else if (c == NAK)
{
crc = 0;
System.Console.WriteLine("checksum mode detected");
return this.startTrans(src, srcsz);

}
else if (c == CAN)
{
#region cancled by remote

if ((c = inputStream.ReadByte()) == CAN)
{
this.writeByte(ACK);
this.flushinput();

System.Console.WriteLine("remote cancelled transfer");
return -1;
}

#endregion
}
}//if
}//for - retry

this.writeByte(CAN);
this.writeByte(CAN);
this.writeByte(CAN);
this.flushinput();
return -2; //no sync
}

#endregion

#region startTrans

private int startTrans(byte[] src, int srcsz)
{

System.Console.WriteLine("starting transfer");

while (true)
{
bufsz = 128;

xbuff[0] = SOH;
xbuff[1] = packetno;
xbuff[2] = (byte)(~packetno);
c = srcsz - len; //len = data already sent
if (c > bufsz) c = bufsz;
if (c >= 0)
{
//clear buffer
xbuff = memset(xbuff, 3, 0, bufsz);

#region fill xbuff with data

if (c == 0)
{
xbuff[3] = CTRLZ;
}
else
{
xbuff = memcpy(xbuff, 3, src, len, c);
if (c < bufsz) xbuff[3 + c] = CTRLZ;
}

#endregion

#region create CRC / Checksum

if (crc == 1)
{
ushort ccrc = CRC16.CRC16_ccitt(xbuff, 3, bufsz);
xbuff[bufsz + 3] = (byte)((ccrc >> 8) & 0xFF);
xbuff[bufsz + 4] = (byte)(ccrc & 0xFF);
}
else
{
byte ccks = 0;
for (i = 3; i < bufsz + 3; ++i)
{
ccks += xbuff[i];
}
xbuff[bufsz + 3] = ccks;
}

#endregion

retry = 0;
bool success = false;

while (retry < MAXRETRANS && !success)
{
retry++;

#region write xbuff out on port

for (i = 0; i < bufsz + 4 + crc; ++i) //crc ? 0:1
{
this.writeByte(xbuff[i]);
}

#endregion

c = inputStream.ReadByte();

if (c >= 0)
{
if (c == ACK)
{

if (this.PaketSent != null)
PaketSent(this, null);

++packetno;
len += bufsz;
success = true; //go ahead in transmition
}
if (c == CAN)
{
if ((c = inputStream.ReadByte()) == CAN)
{
this.writeByte(ACK);
this.flushinput();
return -1; //canceled by remote
}
}
if (c == NAK)
{
//do nothing
}
}
}//while

if (!success)
{
#region xmit error

this.writeByte(CAN);
this.writeByte(CAN);
this.writeByte(CAN);
this.flushinput();
return -4;

#endregion
}
}
else
{
#region transfer completed

for (retry = 0; retry < 10; ++retry)
{
this.writeByte(EOT);

//avoid problem with to short timeout on port
Thread.Sleep(500);

c = inputStream.ReadByte();
if (c == ACK)
break;
}
this.flushinput();
return (c == ACK) ? len : -5;

#endregion
}
}//while
}

#endregion

#endregion

#region a few really cool C-style functions ;)

//writes one single byte
private void writeByte(byte b)
{
byte[] buffer = new byte[1];
buffer[0] = b;
outputStream.Write(buffer, 0, 1);
}

//cleares the input buffer
void flushinput()
{
inputStream.Flush();
}

//sets the first num bytes pointed by buffer to the value specified by c parameter.
byte[] memset(byte[] xbuff, int index, byte c, int num)
{
for (int i = 0; i < num; i++)
{
xbuff[i + index] = c;
}

return xbuff;
}

//copies num bytes from src buffer to memory location pointed by dest.
byte[] memcpy(byte[] dest, int d_i, byte[] src, int d_s, int num)
{
for (int i = 0; i < num; i++)
{
dest[i + d_i] = src[i + d_s];
}

return dest;
}

#endregion
}
}


using System;
//using System.Collections.Generic;
using System.Text;

namespace XModem
{
class CRC16
{
#region Members

//size = 256
private static ushort[] crc16tab = {
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
};

#endregion

#region Implementation

static public ushort CRC16_ccitt(byte[] buf, int index, int len)
{
int counter;
ushort crc = 0;
for( counter = 0; counter < len; counter++)
crc = (ushort)((crc << 8) ^ crc16tab[((crc >> 8) ^ buf[index+counter]) & 0x00FF]);
return crc;
}

#endregion
}
}


using System;
using System.IO;
using System.Text;
using System.Net;
using System.Net.Sockets;

/*
An echo server example that echoes any keys pressed
on the client terminal and prints out the ASCII value
of the key pressed to the server console screen*/

public class EchoKeyServer
{

[STAThread]
public static void Main(System.String[] args)
{
int port = 9050;
byte[] data = new byte[1024];

// create the server socket
System.Net.Sockets.TcpListener temp_tcplistener;
temp_tcplistener = new System.Net.Sockets.TcpListener(port);
temp_tcplistener.Start();
System.Net.Sockets.TcpListener server = temp_tcplistener;

Console.Out.WriteLine("TCPXModem Server 1.0 listening on port " + port);

// wait for an incoming client connection
System.Net.Sockets.TcpClient client = server.AcceptTcpClient();

// handle incoming client connection and exit after transfer

Console.WriteLine("received remote connection from client");

try
{
// create I/O streams for the client connection

System.IO.Stream in_Renamed = (System.IO.Stream) client.GetStream();
System.IO.Stream out_Renamed = (System.IO.Stream) client.GetStream();

// demo of xmodem download

//string fileName = "C://xmodemcs//pic.jpg";
//string fileName = "C://xmodemcs//testdoc.doc";
//string fileName = "C://xmodemcs//xmodem.txt";
string fileName = "C://xmodemcs//ccgms.prg";

/* download file

// works

string msg = "goto receive file mode!";
data = Encoding.ASCII.GetBytes(msg);
out_Renamed.Write(data, 0, data.Length);

msg = "file: " + fileName + " protocol: Xmodem";
data = Encoding.ASCII.GetBytes(msg);
out_Renamed.Write(data, 0, data.Length);

//open file and copy to a byte[] buffer

FileStream fs = new FileStream(fileName, FileMode.Open);
int length = Convert.ToInt32(fs.Length);
byte[] buffer = new byte[length];
fs.Read(buffer, 0, length);

System.Console.WriteLine(fileName + " status = reading file");

XModem.XModem xm = new XModem.XModem(client);
xm.XmodemTransmit(buffer, length);
*/

/* upload file */

// does not work

string msg = "goto send file mode!";
data = Encoding.ASCII.GetBytes(msg);
out_Renamed.Write(data, 0, data.Length);

msg = "file: " + fileName + " protocol: Xmodem";
data = Encoding.ASCII.GetBytes(msg);
out_Renamed.Write(data, 0, data.Length);

XModem.XModem xm = new XModem.XModem(client);
byte[] buffer = xm.XModemReceive();

// todo - scratch file if upload is unsuccessful

//create file

FileStream fs = new FileStream(fileName, FileMode.Create);

try
{
fs.Write(buffer, 0, buffer.Length);
fs.Close();
Console.WriteLine("File saved");
}
catch (Exception e)
{
// todo delete file
Console.WriteLine("File deleted");
fs.Close();
}



//clean up

out_Renamed.Close();
in_Renamed.Close();
xm = null;
}
finally
{
// clean up

client.Close();
server.Stop();
}
}
}


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 28, 2008 6:51 am 
Offline

Joined: Wed Mar 26, 2008 6:04 am
Posts: 4
Never mind. I solve the issue myself. The RX method needed modified so that the server keeps NAKing the client until the upload starts and data beings to flow... then the NAK thread is stopped. Nice little hack as a fix.

I will post the source code for the XServer demo on my website http://www.retrogradebbs.com if anyone wants the XModem.cs class library to use for implementing Xmodem file transfers via a TCP socket.

Now, all I have to do is port the code over to Java now that I understand how it actually all works. :)

It is NOT a trivial project to implement a file transfer protocol over a socket or serial port I have learned. It can be quite frustrating compared to JDBC/MySQL database or Java Swing GUI programming.

.S.


Top
 Profile  
Reply with quote  
PostPosted: Tue May 06, 2008 11:13 pm 
Offline

Joined: Wed Mar 26, 2008 6:04 am
Posts: 4
I have a working Xmodem implemenation for uploading and downloading any type of file over a TCP socket connection. Thanks to cc.trackday.net for the open source class library! No one else offered to help me complete this project. I tried in Java but C#.NET was the working solution. I used Microsoft's JCLA conversion program in Visual Studio.NET to convert the Java source code for my BBS to C#.NET source code so that I could utilize the Xmodem class in C#. A few mods and I converted it from RS-232 to TCP and had a working file transfer library subsystem for my CBBS project after just an hour or two of coding the BBS module in C#.

call retrogradebbs.com today (using CGterm, guruterm, novaterm, or any other C= PETSCII term program) and check out my CBBS project in beta. :)

S0URC3R0R


Top
 Profile  
Reply with quote  
PostPosted: Wed May 07, 2008 12:14 pm 
Offline
Site Admin

Joined: Wed Jan 11, 2006 12:22 pm
Posts: 865
Good job! Will check it out soon.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC + 1 hour [ DST ]


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group