123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- using FxSsh.Messages.Connection;
- using System;
- using System.Diagnostics.Contracts;
- using System.Threading;
- namespace FxSsh.Services
- {
- public abstract class Channel
- {
- protected ConnectionService _connectionService;
- protected EventWaitHandle _sendingWindowWaitHandle = new ManualResetEvent(false);
- public Channel(ConnectionService connectionService,
- uint clientChannelId, uint clientInitialWindowSize, uint clientMaxPacketSize,
- uint serverChannelId)
- {
- Contract.Requires(connectionService != null);
- _connectionService = connectionService;
- ClientChannelId = clientChannelId;
- ClientInitialWindowSize = clientInitialWindowSize;
- ClientWindowSize = clientInitialWindowSize;
- ClientMaxPacketSize = clientMaxPacketSize;
- ServerChannelId = serverChannelId;
- ServerInitialWindowSize = Session.InitialLocalWindowSize;
- ServerWindowSize = Session.InitialLocalWindowSize;
- ServerMaxPacketSize = Session.LocalChannelDataPacketSize;
- }
- public uint ClientChannelId { get; private set; }
- public uint ClientInitialWindowSize { get; private set; }
- public uint ClientWindowSize { get; protected set; }
- public uint ClientMaxPacketSize { get; private set; }
- public uint ServerChannelId { get; private set; }
- public uint ServerInitialWindowSize { get; private set; }
- public uint ServerWindowSize { get; protected set; }
- public uint ServerMaxPacketSize { get; private set; }
- public bool ClientClosed { get; private set; }
- public bool ClientMarkedEof { get; private set; }
- public bool ServerClosed { get; private set; }
- public bool ServerMarkedEof { get; private set; }
- public event EventHandler<byte[]> DataReceived;
- public event EventHandler EofReceived;
- public event EventHandler CloseReceived;
- public void SendData(byte[] data)
- {
- Contract.Requires(data != null);
- if (data.Length == 0)
- {
- return;
- }
- var msg = new ChannelDataMessage();
- msg.RecipientChannel = ClientChannelId;
- var total = (uint)data.Length;
- var offset = 0L;
- byte[] buf = null;
- do
- {
- var packetSize = Math.Min(Math.Min(ClientWindowSize, ClientMaxPacketSize), total);
- if (packetSize == 0)
- {
- _sendingWindowWaitHandle.WaitOne();
- continue;
- }
- if (buf == null || packetSize != buf.Length)
- buf = new byte[packetSize];
- Array.Copy(data, offset, buf, 0, packetSize);
- msg.Data = buf;
- _connectionService._session.SendMessage(msg);
- ClientWindowSize -= packetSize;
- total -= packetSize;
- offset += packetSize;
- } while (total > 0);
- }
- public void SendEof()
- {
- if (ServerMarkedEof)
- return;
- ServerMarkedEof = true;
- var msg = new ChannelEofMessage { RecipientChannel = ClientChannelId };
- _connectionService._session.SendMessage(msg);
- }
- public void SendClose(uint? exitCode = null)
- {
- if (ServerClosed)
- return;
- ServerClosed = true;
- if (exitCode.HasValue)
- _connectionService._session.SendMessage(new ExitStatusMessage { RecipientChannel = ClientChannelId, ExitStatus = exitCode.Value });
- _connectionService._session.SendMessage(new ChannelCloseMessage { RecipientChannel = ClientChannelId });
- CheckBothClosed();
- }
- internal void OnData(byte[] data)
- {
- Contract.Requires(data != null);
- ServerAttemptAdjustWindow((uint)data.Length);
- if (DataReceived != null)
- DataReceived(this, data);
- }
- internal void OnEof()
- {
- ClientMarkedEof = true;
- if (EofReceived != null)
- EofReceived(this, EventArgs.Empty);
- }
- internal void OnClose()
- {
- ClientClosed = true;
- if (CloseReceived != null)
- CloseReceived(this, EventArgs.Empty);
- CheckBothClosed();
- }
- internal void ClientAdjustWindow(uint bytesToAdd)
- {
- ClientWindowSize += bytesToAdd;
- // pulse multithreadings in same time and unsignal until thread switched
- // don't try to use AutoResetEvent
- _sendingWindowWaitHandle.Set();
- Thread.Sleep(1);
- _sendingWindowWaitHandle.Reset();
- }
- private void ServerAttemptAdjustWindow(uint messageLength)
- {
- ServerWindowSize -= messageLength;
- if (ServerWindowSize <= ServerMaxPacketSize)
- {
- _connectionService._session.SendMessage(new ChannelWindowAdjustMessage
- {
- RecipientChannel = ClientChannelId,
- BytesToAdd = ServerInitialWindowSize - ServerWindowSize
- });
- ServerWindowSize = ServerInitialWindowSize;
- }
- }
- private void CheckBothClosed()
- {
- if (ClientClosed && ServerClosed)
- {
- ForceClose();
- }
- }
- internal void ForceClose()
- {
- _connectionService.RemoveChannel(this);
- _sendingWindowWaitHandle.Set();
- _sendingWindowWaitHandle.Close();
- }
- }
- }
|