xmodem.cs
上传用户:szltgg
上传日期:2019-05-16
资源大小:604k
文件大小:11k
源码类别:

Telnet服务器

开发平台:

C#

  1. /*
  2. * Copyright (c) 2005 Poderosa Project, All Rights Reserved.
  3. * $Id: xmodem.cs,v 1.2 2005/04/20 08:45:48 okajima Exp $
  4. */
  5. using System;
  6. using System.Diagnostics;
  7. using System.IO;
  8. using System.Threading;
  9. using Poderosa.Connection;
  10. namespace Poderosa.Terminal
  11. {
  12. /// <summary>
  13. /// xmodem 偺奣梫偺愢柧偱偡丅
  14. /// </summary>
  15. public abstract class XModem : IModalTerminalTask {
  16. public const byte SOH = 1;
  17. public const byte STX = 2;
  18. public const byte EOT = 4;
  19. public const byte ACK = 6;
  20. public const byte NAK = 21;
  21. public const byte CAN = 24;
  22. public const byte SUB = 26;
  23. //忬懺捠抦
  24. public const int NOTIFY_SUCCESS = 0;
  25. public const int NOTIFY_PROGRESS = 1;
  26. public const int NOTIFY_ERROR = 2;
  27. public const int NOTIFY_TIMEOUT = 3;
  28. //CRC
  29. public static ushort CalcCRC(byte[] data, int offset, int length) {
  30. ushort crc = 0;
  31. for(int i=0; i<length; i++) {
  32. byte d = data[offset+i];
  33. /*
  34. int count = 8;
  35. while(--count>=0) {
  36. if((crc & 0x8000)!=0) {
  37. crc <<= 1;
  38. crc += (((d<<=1) & 0x0400) != 0);
  39. crc ^= 0x1021;
  40. }
  41. else {
  42. crc <<= 1;
  43. crc += (((d<<=1) & 0x0400) != 0);
  44. }
  45. }
  46. */
  47. crc ^= (ushort)((ushort)d << 8);
  48. for(int j=1; j<=8; j++) {
  49. if((crc & 0x8000)!=0)
  50. crc = (ushort)((crc<<1) ^ (ushort)0x1021);
  51. else
  52. crc <<= 1;
  53. }
  54. }
  55. return crc;
  56. }
  57. protected ConnectionTag _tag;
  58. protected string _fileName;
  59. protected byte _sequenceNumber;
  60. protected long _processedLength;
  61. protected bool _crcEnabled;
  62. protected Stream _stream;
  63. protected Timer _timer;
  64. protected IntPtr _notifyTarget;
  65. public XModem(ConnectionTag tag, string fn) {
  66. _tag = tag;
  67. _fileName = fn;
  68. _sequenceNumber = 1;
  69. }
  70. public bool CRCEnabled {
  71. get {
  72. return _crcEnabled;
  73. }
  74. }
  75. public IntPtr NotifyTarget {
  76. get {
  77. return _notifyTarget;
  78. }
  79. set {
  80. _notifyTarget = value;
  81. }
  82. }
  83. public abstract void Start();
  84. public abstract void Abort();
  85. public abstract void Input(byte[] data, int offset, int count);
  86. public bool CanReceive {
  87. get {
  88. return true;
  89. }
  90. }
  91. public string Caption {
  92. get {
  93. return "XMODEM";
  94. }
  95. }
  96. protected void NotifyStatus(int wparam, int lparam) {
  97. if(_notifyTarget==IntPtr.Zero) return;
  98. Win32.SendMessage(_notifyTarget, GConst.WMG_XMODEM_UPDATE_STATUS, new IntPtr(wparam), new IntPtr(lparam));
  99. }
  100. }
  101. public class XModemReceiver : XModem {
  102. private int _retryCount;
  103. private MemoryStream _buffer;
  104. //private FileStream _debugStream;
  105. private const int CRC_TIMEOUT = 1;
  106. private const int NEGOTIATION_TIMEOUT = 2;
  107. public XModemReceiver(ConnectionTag tag, string filename) : base(tag, filename) {
  108. _stream = new FileStream(_fileName, FileMode.Create, FileAccess.Write);
  109. }
  110. public override void Start() {
  111. _tag.ModalTerminalTask = this;
  112. _timer = new Timer(new TimerCallback(OnTimeout), CRC_TIMEOUT, 3000, Timeout.Infinite);
  113. _crcEnabled = true;
  114. _tag.Connection.Write(new byte[] { (byte)'C' }); //CRC儌乕僪偱僩儔僀
  115. //_debugStream = new FileStream("C:\IOPort\xmodemtest.bin", FileMode.Create, FileAccess.Write);
  116. }
  117. private void OnTimeout(object state) {
  118. _timer.Dispose();
  119. _timer = null;
  120. switch((int)state){ 
  121. case CRC_TIMEOUT:
  122. _crcEnabled = false;
  123. _timer = new Timer(new TimerCallback(OnTimeout), NEGOTIATION_TIMEOUT, 5000, Timeout.Infinite);
  124. _tag.Connection.Write(new byte[] { NAK });
  125. break;
  126. case NEGOTIATION_TIMEOUT:
  127. _tag.Connection.Write(new byte[] { CAN });
  128. GEnv.InterThreadUIService.Warning(GEnv.Strings.GetString("Message.XModem.StartTimedOut"));
  129. NotifyStatus(NOTIFY_TIMEOUT, 0);
  130. Exit();
  131. break;
  132. }
  133. }
  134. private void Exit() {
  135. _tag.ModalTerminalTask = null;
  136. _stream.Close();
  137. if(_timer!=null) _timer.Dispose();
  138. }
  139. public override void Abort() {
  140. _tag.Connection.Write(new byte[] { CAN });
  141. Exit();
  142. }
  143. public override void Input(byte[] data, int offset, int count) {
  144. if(_timer!=null) {
  145. _timer.Dispose();
  146. _timer = null;
  147. }
  148. //Debug.WriteLine(String.Format("Received {0}", count));
  149. //_debugStream.Write(data, offset, count);
  150. //_debugStream.Flush();
  151. AdjustBuffer(ref data, ref offset, ref count);
  152. byte head = data[offset];
  153. if(head==EOT) { //successfully exit
  154. _tag.Connection.Write(new byte[] { ACK });
  155. GEnv.InterThreadUIService.Information(GEnv.Strings.GetString("Message.XModem.ReceiveComplete"));
  156. NotifyStatus(NOTIFY_SUCCESS, 0);
  157. //_debugStream.Close();
  158. Exit();
  159. }
  160. else {
  161. int required = 3+(head==STX? 1024 : 128)+(_crcEnabled? 2 : 1);
  162. if(required>count) {
  163. ReserveBuffer(data, offset, count); //搑拞偱愗傟偰偄偨応崌
  164. //Debug.WriteLine(String.Format("Reserving #{0} last={1} offset={2} count={3}", seq, last, offset, count));
  165. return;
  166. }
  167. byte seq  = data[offset+1];
  168. byte neg  = data[offset+2];
  169. if(seq!=_sequenceNumber || seq+neg!=255) {
  170. Abort();
  171. GEnv.InterThreadUIService.Warning(GEnv.Strings.GetString("Message.XModem.SequenceError"));
  172. NotifyStatus(NOTIFY_ERROR, 0);
  173. }
  174. else {
  175. //Debug.WriteLine(String.Format("Processing #{0}", seq));
  176. bool success;
  177. int  body_offset = offset+3;
  178. int  body_len = head==STX? 1024 : 128;
  179. int  checksum_offset = offset+3+body_len;
  180. if(_crcEnabled) {
  181. ushort sent = (ushort)( (((ushort)data[checksum_offset])<<8) + (ushort)data[checksum_offset+1] );
  182. ushort sum  = CalcCRC(data, body_offset, body_len);
  183. success = (sent==sum);
  184. }
  185. else {
  186. byte sent = data[checksum_offset];
  187. byte sum = 0;
  188. for(int i=body_offset; i<checksum_offset; i++) sum += data[i];
  189. success = (sent==sum);
  190. }
  191. _buffer = null; //僽儘僢僋偛偲偵ACK傪懸偮巇條側偺偱丄傕傜偭偰偒偨僨乕僞偑暋悢僽儘僢僋偵傑偨偑傞偙偲偼側偄丅偟偨偑偭偰偙偙偱攋婞偟偰峔傢側偄丅
  192. if(success) {
  193. _tag.Connection.Write(new byte[] { ACK });
  194. _sequenceNumber++;
  195. int t = checksum_offset-1;
  196. while(t>=body_offset && data[t]==26) t--; //Ctrl+Z偱杽傑偭偰偄傞偲偙傠偼柍帇
  197. int len = t+1 - body_offset;
  198. _stream.Write(data, body_offset, len);
  199. _processedLength += len;
  200. NotifyStatus(NOTIFY_PROGRESS, (int)_processedLength);
  201. _retryCount = 0;
  202. }
  203. else {
  204. //_debugStream.Close();
  205. if(++_retryCount==3) { //傕偆偁偒傜傔傞
  206. Abort();
  207. GEnv.InterThreadUIService.Warning(GEnv.Strings.GetString("Message.XModem.CheckSumError"));
  208. NotifyStatus(NOTIFY_ERROR, 0);
  209. }
  210. else {
  211. _tag.Connection.Write(new byte[] { NAK });
  212. }
  213. }
  214. }
  215. }
  216. }
  217. private void ReserveBuffer(byte[] data, int offset, int count) {
  218. _buffer = new MemoryStream();
  219. _buffer.Write(data, offset, count);
  220. }
  221. private void AdjustBuffer(ref byte[] data, ref int offset, ref int count) {
  222. if(_buffer==null || _buffer.Position==0) return;
  223. _buffer.Write(data, offset, count);
  224. count = (int)_buffer.Position;
  225. _buffer.Close();
  226. data = _buffer.ToArray();
  227. Debug.Assert(data.Length==count);
  228. offset = 0;
  229. }
  230. }
  231. public class XModemSender : XModem {
  232. private bool _negotiating;
  233. private int _retryCount;
  234. private byte[] _body;
  235. private int _offset;
  236. private int _nextOffset;
  237. //private FileStream _debugStream;
  238. private const int NEGOTIATION_TIMEOUT = 1;
  239. public int TotalLength {
  240. get {
  241. return _body.Length;
  242. }
  243. }
  244. public XModemSender(ConnectionTag tag, string filename) : base(tag, filename) {
  245. _body = new byte[new FileInfo(filename).Length];
  246. FileStream strm = new FileStream(filename, FileMode.Open, FileAccess.Read);
  247. strm.Read(_body, 0, _body.Length);
  248. strm.Close();
  249. }
  250. public override void Start() {
  251. _tag.ModalTerminalTask = this;
  252. _timer = new Timer(new TimerCallback(OnTimeout), NEGOTIATION_TIMEOUT, 60000, Timeout.Infinite);
  253. _negotiating = true;
  254. //_tag.Connection.WriteChars(TerminalUtil.NewLineChars(_tag.Connection.Param.TransmitNL));
  255. }
  256. private void OnTimeout(object state) {
  257. _timer.Dispose();
  258. _timer = null;
  259. switch((int)state){ 
  260. case NEGOTIATION_TIMEOUT:
  261. GEnv.InterThreadUIService.Warning(GEnv.Strings.GetString("Message.XModem.StartTimedOut"));
  262. NotifyStatus(NOTIFY_TIMEOUT, 0);
  263. Exit();
  264. break;
  265. }
  266. }
  267. private void Exit() {
  268. _tag.ModalTerminalTask = null;
  269. if(_timer!=null) _timer.Dispose();
  270. }
  271. public override void Abort() {
  272. _tag.Connection.Write(new byte[] { CAN });
  273. Exit();
  274. }
  275. public override void Input(byte[] data, int offset, int count) {
  276. if(_timer!=null) {
  277. _timer.Dispose();
  278. _timer = null;
  279. }
  280. if(_negotiating) {
  281. for(int i=0; i<count; i++) {
  282. byte t = data[offset+i];
  283. if(t==NAK || t==(byte)'C') {
  284. _crcEnabled = t==(byte)'C';
  285. _negotiating = false;
  286. _sequenceNumber = 1;
  287. _offset = _nextOffset = 0;
  288. break;
  289. }
  290. }
  291. if(_negotiating) return; //偁偨傑偑偒偰偄側偄
  292. }
  293. else {
  294. byte t = data[offset];
  295. if(t==ACK) {
  296. _sequenceNumber++;
  297. _retryCount = 0;
  298. if(_offset==_body.Length) { //successfully exit
  299. Exit();
  300. GEnv.InterThreadUIService.Information(GEnv.Strings.GetString("Message.XModem.SendComplete"));
  301. NotifyStatus(NOTIFY_SUCCESS, 0);
  302. return;
  303. }
  304. _offset = _nextOffset;
  305. }
  306. else if(t!=NAK || (++_retryCount==3)) {
  307. Abort();
  308. GEnv.InterThreadUIService.Warning(GEnv.Strings.GetString("Message.XModem.BlockStartError"));
  309. NotifyStatus(NOTIFY_ERROR, 0);
  310. return;
  311. }
  312. }
  313. if(_nextOffset>=_body.Length) { //last
  314. _tag.Connection.Write(new byte[] { EOT });
  315. _offset = _body.Length;
  316. }
  317. else {
  318. int len = 128;
  319. if(_crcEnabled && _offset+1024<=_body.Length) len = 1024;
  320. byte[] buf = new byte[3+len+(_crcEnabled? 2 : 1)];
  321. buf[0] = len==128? SOH : STX;
  322. buf[1] = (byte)_sequenceNumber;
  323. buf[2] = (byte)(255 - buf[1]);
  324. int body_len = Math.Min(len, _body.Length-_offset);
  325. Array.Copy(_body, _offset, buf, 3, body_len);
  326. for(int i=body_len; i<len; i++)
  327. buf[3+i] = 26; //padding
  328. if(_crcEnabled) {
  329. ushort sum  = CalcCRC(buf, 3, len);
  330. buf[3+len  ] = (byte)(sum >> 8);
  331. buf[3+len+1] = (byte)(sum & 0xFF);
  332. }
  333. else {
  334. byte sum = 0;
  335. for(int i=0; i<len; i++) sum += buf[3+i];
  336. buf[3+len] = sum;
  337. }
  338. _nextOffset = _offset + len;
  339. _tag.Connection.Write(buf, 0, buf.Length);
  340. NotifyStatus(NOTIFY_PROGRESS, _nextOffset);
  341. //Debug.WriteLine("Transmitted "+_sequenceNumber+" " +_offset);
  342. }
  343. }
  344. }
  345. }