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 >>

& 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 <<

^ crc16tab[((crc >>

^ 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();
}
}
}