This project is read-only.

Async SCP upload with progress

Oct 5, 2013 at 5:23 PM
First, let me say that this library is a true savior and allowed me to do everything that I needed for my project! Thanks!

I'm trying to upload a file using SCP to a Linux server (unfortunately, FTP and SFTP aren't an option) using C#. The upload goes fine, however, while it's uploading, the GUI is frozen and I can't use it until the upload is complete. Is there a way to have the upload be complete while still being able to use the GUI? Also, while the upload is going one, is there a way to have a progress bar that's being complete with the MB remaining?

I've also noticed that the freezing happens when I'm sending commands that are taking a few seconds to complete and return the result. Is there a way to send commands in an async way?

Again, thank you so much for this awesome library!
Oct 9, 2013 at 4:02 PM
Edited Oct 9, 2013 at 4:02 PM
You can use ShellStream with AsyncRead and AsyncWrite.
Oct 9, 2013 at 4:52 PM
Thanks for the reply! Do you know where can I find an example on how to use that?
Oct 10, 2013 at 7:46 AM
Hi,

Just a quick&paste for AsyncRead from a project.
Hope it gives you a hint what to do.

Init and connect SshClient + ShellStream
SshClient sshClient = new SshClient(Hostname, Port,username, password);
sshclient.Connect();
ShellStream _shell = sshClient.CreateShellStream("xterm", 80, 24, 800, 600, 1024);
_shell.DataReceived += StartAsyncRead;
The AsyncRead
private void StartAsyncRead(object sender, EventArgs e)
{
 byte[] _data = new byte[2048];
  _shell.BeginRead(_data, 0, _data.Length, OnReadCompletion, new MyAsyncInfo(_data, _shell));
}
The Callback
        private void OnReadCompletion(IAsyncResult ar)
        {
                var mai = (MyAsyncInfo)ar.AsyncState;
                int datalen = mai.Stream.EndRead(ar);
                string line = sshClient.ConnectionInfo.Encoding.GetString(mai.ByteArray, 0, datalen);
                Debug.WriteLine("Received: " + line);
        }
The Async Info
    public class MyAsyncInfo
    {
        public Byte[] ByteArray { get; set; }
        public ShellStream Stream { get; set; }

        public MyAsyncInfo(Byte[] array, ShellStream stream)
        {
            ByteArray = array;
            Stream = stream;
        }
    }
To Write/SendCommands I use:
(Can be also done with AsyncWrite I guess, but is not neccessary when your commands don't have thousand of chars ;))
        public void SendCommand(string command)
        {
            Debug.WriteLine("Sending Command: " + command);
            if (_shell != null && _shell.CanWrite)
                _shell.Write(command + "\n");
        }
Oct 11, 2013 at 8:03 PM
Thanks for the info. I know I'm probably doing something dumb, but I can't get the code to work (I only recently started working with C#). Here's my whole code (I've made a C# Form):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Renci.SshNet;
using System.Diagnostics;

namespace SCP_Async_Test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            SshClient sshClient = new SshClient(serverIPAddressTextBox.Text, serverUsernameTextBox.Text, serverPasswordTextBox.Text);
            sshClient.Connect();
            ShellStream _shell = sshClient.CreateShellStream("xterm", 80, 24, 800, 600, 1024);
            _shell.DataReceived += StartAsyncRead;
        }

        private void installServerBtn_Click(object sender, EventArgs e)
        {
            SshClient sshClient = new SshClient(serverIPAddressTextBox.Text, serverUsernameTextBox.Text, serverPasswordTextBox.Text);
            sshClient.Connect();
            ShellStream _shell = sshClient.CreateShellStream("xterm", 80, 24, 800, 600, 1024);
            _shell.DataReceived += StartAsyncRead;
        }

        private void StartAsyncRead(object sender, EventArgs e)
        {
            byte[] _data = new byte[2048];
            _shell.BeginRead(_data, 0, _data.Length, OnReadCompletion, new MyAsyncInfo(_data, _shell));
        }

        private void OnReadCompletion(IAsyncResult ar)
        {
            var mai = (MyAsyncInfo)ar.AsyncState;
            int datalen = mai.Stream.EndRead(ar);
            string line = sshClient.ConnectionInfo.Encoding.GetString(mai.ByteArray, 0, datalen);
            Debug.WriteLine("Received: " + line);
        }

        public class MyAsyncInfo
        {
            public Byte[] ByteArray { get; set; }
            public ShellStream Stream { get; set; }

            public MyAsyncInfo(Byte[] array, ShellStream stream)
            {
                ByteArray = array;
                Stream = stream;
            }
        }

        public void SendCommand(string command)
        {
            Debug.WriteLine("Sending Command: " + command);
            if (_shell != null && _shell.CanWrite)
                _shell.Write(command + "\n");
        }
    }
}
VS is saying that the variables _shell and sshClient don't exist outside the Public Form1() function. Also, I see that you're creating a new byte array in the StartAsyncRead method, however, what do you do if you need to specify and actual file? Do you just change "_data" to FileInfo("file.zip")?

Thank you so much for the help!
Oct 15, 2013 at 6:33 AM
OK. This one I'm sure you will figure out :)
Hint: Watch the Scope Variables/Properties are accesible.

Are you sure, you want to work with Async when basic skills are missing?
No offense, but Async is quite a tough topic!