Channel.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using FxSsh.Messages.Connection;
  2. using System;
  3. using System.Diagnostics.Contracts;
  4. using System.Threading;
  5. namespace FxSsh.Services
  6. {
  7. public abstract class Channel
  8. {
  9. protected ConnectionService _connectionService;
  10. protected EventWaitHandle _sendingWindowWaitHandle = new ManualResetEvent(false);
  11. public Channel(ConnectionService connectionService,
  12. uint clientChannelId, uint clientInitialWindowSize, uint clientMaxPacketSize,
  13. uint serverChannelId)
  14. {
  15. Contract.Requires(connectionService != null);
  16. _connectionService = connectionService;
  17. ClientChannelId = clientChannelId;
  18. ClientInitialWindowSize = clientInitialWindowSize;
  19. ClientWindowSize = clientInitialWindowSize;
  20. ClientMaxPacketSize = clientMaxPacketSize;
  21. ServerChannelId = serverChannelId;
  22. ServerInitialWindowSize = Session.InitialLocalWindowSize;
  23. ServerWindowSize = Session.InitialLocalWindowSize;
  24. ServerMaxPacketSize = Session.LocalChannelDataPacketSize;
  25. }
  26. public uint ClientChannelId { get; private set; }
  27. public uint ClientInitialWindowSize { get; private set; }
  28. public uint ClientWindowSize { get; protected set; }
  29. public uint ClientMaxPacketSize { get; private set; }
  30. public uint ServerChannelId { get; private set; }
  31. public uint ServerInitialWindowSize { get; private set; }
  32. public uint ServerWindowSize { get; protected set; }
  33. public uint ServerMaxPacketSize { get; private set; }
  34. public bool ClientClosed { get; private set; }
  35. public bool ClientMarkedEof { get; private set; }
  36. public bool ServerClosed { get; private set; }
  37. public bool ServerMarkedEof { get; private set; }
  38. public event EventHandler<byte[]> DataReceived;
  39. public event EventHandler EofReceived;
  40. public event EventHandler CloseReceived;
  41. public void SendData(byte[] data)
  42. {
  43. Contract.Requires(data != null);
  44. if (data.Length == 0)
  45. {
  46. return;
  47. }
  48. var msg = new ChannelDataMessage();
  49. msg.RecipientChannel = ClientChannelId;
  50. var total = (uint)data.Length;
  51. var offset = 0L;
  52. byte[] buf = null;
  53. do
  54. {
  55. var packetSize = Math.Min(Math.Min(ClientWindowSize, ClientMaxPacketSize), total);
  56. if (packetSize == 0)
  57. {
  58. _sendingWindowWaitHandle.WaitOne();
  59. continue;
  60. }
  61. if (buf == null || packetSize != buf.Length)
  62. buf = new byte[packetSize];
  63. Array.Copy(data, offset, buf, 0, packetSize);
  64. msg.Data = buf;
  65. _connectionService._session.SendMessage(msg);
  66. ClientWindowSize -= packetSize;
  67. total -= packetSize;
  68. offset += packetSize;
  69. } while (total > 0);
  70. }
  71. public void SendEof()
  72. {
  73. if (ServerMarkedEof)
  74. return;
  75. ServerMarkedEof = true;
  76. var msg = new ChannelEofMessage { RecipientChannel = ClientChannelId };
  77. _connectionService._session.SendMessage(msg);
  78. }
  79. public void SendClose(uint? exitCode = null)
  80. {
  81. if (ServerClosed)
  82. return;
  83. ServerClosed = true;
  84. if (exitCode.HasValue)
  85. _connectionService._session.SendMessage(new ExitStatusMessage { RecipientChannel = ClientChannelId, ExitStatus = exitCode.Value });
  86. _connectionService._session.SendMessage(new ChannelCloseMessage { RecipientChannel = ClientChannelId });
  87. CheckBothClosed();
  88. }
  89. internal void OnData(byte[] data)
  90. {
  91. Contract.Requires(data != null);
  92. ServerAttemptAdjustWindow((uint)data.Length);
  93. if (DataReceived != null)
  94. DataReceived(this, data);
  95. }
  96. internal void OnEof()
  97. {
  98. ClientMarkedEof = true;
  99. if (EofReceived != null)
  100. EofReceived(this, EventArgs.Empty);
  101. }
  102. internal void OnClose()
  103. {
  104. ClientClosed = true;
  105. if (CloseReceived != null)
  106. CloseReceived(this, EventArgs.Empty);
  107. CheckBothClosed();
  108. }
  109. internal void ClientAdjustWindow(uint bytesToAdd)
  110. {
  111. ClientWindowSize += bytesToAdd;
  112. // pulse multithreadings in same time and unsignal until thread switched
  113. // don't try to use AutoResetEvent
  114. _sendingWindowWaitHandle.Set();
  115. Thread.Sleep(1);
  116. _sendingWindowWaitHandle.Reset();
  117. }
  118. private void ServerAttemptAdjustWindow(uint messageLength)
  119. {
  120. ServerWindowSize -= messageLength;
  121. if (ServerWindowSize <= ServerMaxPacketSize)
  122. {
  123. _connectionService._session.SendMessage(new ChannelWindowAdjustMessage
  124. {
  125. RecipientChannel = ClientChannelId,
  126. BytesToAdd = ServerInitialWindowSize - ServerWindowSize
  127. });
  128. ServerWindowSize = ServerInitialWindowSize;
  129. }
  130. }
  131. private void CheckBothClosed()
  132. {
  133. if (ClientClosed && ServerClosed)
  134. {
  135. ForceClose();
  136. }
  137. }
  138. internal void ForceClose()
  139. {
  140. _connectionService.RemoveChannel(this);
  141. _sendingWindowWaitHandle.Set();
  142. _sendingWindowWaitHandle.Close();
  143. }
  144. }
  145. }