delegatevoid

C#/.NET implementation of API on Bitbucket

Recommended Posts

Dear Delegatevoid,

 

as I'm new to OOP programming, and since your (excellent) implementation relies heavily on it's concepts, I'd like to clarify an issue I'm having.

 

I have developed an application which:

1 - starts an instance of Btsync.exe with the supplied config file using Process.Start()

2 - creates a folder C:\test

3 - makes use of the AddFolder method to have that folder synced (a read-only key is provided with my application)

4 - closes itself (btsync will remain running)

 

The problem is, my program won't accomplish step 3 unless I give it some time before the program closes (either using Console.Read() or Thread.Sleep() ). I do believe this is caused by the method being asynchronous. Is it correct? What can I do about this, other than pausing the execution for a little while before the program exits?

 

Anyone else please feel free to help me on this as well. Thanks!

 

Answer

 

I was using

static async void AddFolderToSync()

and calling that method under the Main method of my console app. Now I've switched to

static async Task AddFolderToSync()

and it's called like this

AddFolderToSync().Wait()

and my problem is solved. :D

 

Reference: http://stackoverflow.com/questions/3840795/console-app-terminating-before-async-call-completion

Share this post


Link to post
Share on other sites

Hi,

 

You are correct to say that the methods in the library are all asynchronous.

By default this means fire and forget. You start a Task which gets executed somewhere else

and your applications imply continues to execute whatever line of code follows.

 

I'm sure you are using a console application so here's what's happening

  1. Your application starts at the entry point public static void Main()
  2. It executes the code in that method, starting with whatever logic you use to start BTSync
  3. It then creates the folder (be careful where you create it, your application may not have access to that location)
  4. Then you start a new task by calling client.AddFolder but that method is asynchronous, so it (to put it simply) gets executed somewhere else and your main thread does not wait for it, instead it continues to execute the next line in your main method. If there is nothing there, your application will close and the task along with it.

While your answer, posted above, would work, it is not the correct way of using the code.

Instead you should use the async/await pattern in C# (http://msdn.microsoft.com/en-us/library/hh191443.aspx)

 

Consider the following (a little over simplified example):

using Arendee.BTSyncLib;using System;using System.IO;using System.Threading;using System.Threading.Tasks;namespace BTSyncExample{    class Program    {        private bool exitApplication;        private string server;        private string username;        private string password;        private string deviceName;        private int port;        static void Main(string[] args)        {            new Program();        }        new Program()        {            //Replace the values here to fit your setup            Run("localhost", 8888, "spongebob", "squarepants", "HelloBTSync");            while (!exitApplication)            {                Thread.Sleep(500);            }        }        public async void Run(string server, int port, string username = "",                               string password = "", string deviceName = "YourAppName")        {            this.server = server;            this.port = port;            this.username = username;            this.password = password;            this.deviceName = deviceName;            StartBTSync();            await AddBTSyncFolder("c:\\test", "yoursecrethere");            Console.Write("Press any key to continue...");            Console.ReadKey();            exitApplication = true;        }        private async Task AddBTSyncFolder(string path, string secret)        {            try            {                if (!Directory.Exists(path))                {                    Console.WriteLine("Creating directory: '{0}'", path);                    Directory.CreateDirectory(path);                }                Console.WriteLine("Adding the folder to BTSync");                var client = new BTSyncClient(server, port, username, password, deviceName);                await client.AddFolder(path, secret);                            }            catch (Exception ex)            {                Console.ForegroundColor = ConsoleColor.Red;                Console.WriteLine("Add folder failed: {0}", ex.Message);                Console.ForegroundColor = ConsoleColor.White;            }        }        private void StartBTSync()        {            //Do whatever you need to to start btsync        }    }}

Start by looking at the AddBTSyncFolder method. It's signature includes the async keyword and returns a Task.

Simply put, a Task is something that will get executed somewhere else and you can wait for it to complete.

After creating the folder it creates an instance of BTSyncClient and calls AddFolder

await client.AddFolder(path, secret);

Note the await keyword. This means the method execution will stop at this line, the AddFolder task will be started,
and when it has been completed, method execution will resume as if nothing happened.

 

(Note that the AddBTSyncFolder function is not a clean function, it does two things, creating a folder and adding it to BTSync,
 normally a function should do one thing and one thing only, but for the purpose of this example it should be fine)

 

The Run  method is also defined as async but it does not return a Task. An async method that does not return a task
is truly fire and forget. It is started and there is no way to wait for it. It has to be like this because, otherwise any method
calling Run would have to be asynchronous as well. The main entry point for your application and the constructor of the
Program class cannot be asynchronous, so Run does not return a Task. (For more info on this check out articles referring
to async/await being contagious).

 

So finally there is the constructor of the Program class which calls Run.
It will start executing the code in Run but it will not wait and if you do nothing after that line,
your application will, once again, simply exit.

 

So I've included a while loop which will put the main thread to sleep until exitApplication is set to true.

 

It may seem a little counter-intuitive, but that's because async/await does not really lend itself for use
in a console application. That doesn't mean it shouldn't be used in console applications, it just means that
there's a little extra work to be done to make it behave properly. A desktop application for example, does not
exit until all windows have been closed so there is no need for a wait loop. The async/await pattern is particularly
useful in desktop application as a way to perform long running tasks without freezing the UI thread.
(Remember the infamous 'Application xxx is not responding')

 

Once you get the hang of async/await you'll wonder how you ever got things done without it,
so if you can, invest some time in learning more about it, you will not regret it.

 

I hope this helps,

and if you have any more questions, just ask. 

Share this post


Link to post
Share on other sites

Thank you for elucidating, mate.

 

Do you see any reason I should not do the following?

public async Task Run()Run("localhost", 8888, "spongebob", "squarepants", "HelloBTSync").Wait;

This eliminates the need of using Thread.Sleep() and seems to be a better approach.

 

Also, shouldn't I instantiate BTSyncClient before the beginning of the try clause?

Share this post


Link to post
Share on other sites

In this particular case, if your application doesn't do anything else at the same time, you can get away with using .Wait

When you build bigger applications or desktop applications .Wait() will not be suitable.

 

As for the BTSyncClient instance, there's no need to do that before the try.

In fact, if you have a try/catch in a method you should always try and make it so that the try is

the very first thing in the method.

Share this post


Link to post
Share on other sites

Btw, I think I should define the constructor as

public Program() {definition}

 

instead of

new Program() {definition}

 

or simply as

Program() {definition}

 

right? Sorry, this is indeed a newbie question.

Share this post


Link to post
Share on other sites

Man, I'd like to let you know about an issue with the new version of BTSync (v1.4).

 

Have a look at this snippet:

var btSyncClient = new BTSyncClient(SYNC_SERVER, SYNC_PORT, SYNC_USERNAME, SYNC_PASSWORD, syncName);Console.WriteLine("New API instance created");//  applies transfer control, if defined by bool variableif (transferSpeedLimit){    var btSyncPrefs = new Arendee.BTSyncLib.Model.SyncPreferences();    btSyncPrefs.UploadLimit = uploadSpeedLimit;    await btSyncClient.SetPreferences(btSyncPrefs);}

This will throw an exception: ->  Could not convert string to integer: *5. Path 'recv_buf_size', line 1, position 1389  <-

 

I'll not look into this myself because I had to decide against using BTSync.

Share this post


Link to post
Share on other sites

Do you have an open source project setup for the development of this on github?

 

Last I checked the requirement was .net 4.5+ do you have a version avalible for 4?

Share this post


Link to post
Share on other sites

@marck321 You are correct, thank you for pointing it out.

 

Apparently the response to this API request has changed, I will have to check what is going on.

 

@lg0

The open source repository for this projects can be found on BitBucket (I am not a fan of GIT)

https://bitbucket.org/timothyp/arendee-btsynclib

 

Feel free to post bug/feature requests there.

 

As for 4.0 support, I'm afraid not, since the library relies heavily on async/await.
That being said, you could backport it to use .NET 4.0 and the Async CTP for .NET 4.0,

however I wouldn't suggest it. Today Windows XP is the only version of Windows not supported by .NET 4.5
and nobody should be running that anymore anyway. 

 

If you do want to backport it, perhaps you can fork the repository in GitHub, 

create a new branch for .net 4.0 and if it's good I'll pull it back into the main repo

And of course I'd be more than willing to help out if need be.


@mark321 I looked into it some more, it's actually a bug in the new version of BTSync

 

wCHuJ.jpg

 

The values marked with a * should actually just be integer values,

and since the latest version they are not. Will post this as a bug

 

Update: I've posted it here: http://forum.bittorrent.com/topic/31616-bug-preference-values-in-btsync-14x-contain/

Share this post


Link to post
Share on other sites

Hi,

thank you for sharing this implementation with us.

 

I have a problem with the SetPreferences() method. Whenever I try to set the variables some of them are not saved.

Here is an example:

SyncPreferences pref = new SyncPreferences();pref.DeviceName = tb_deviceName.Text;pref.UseUPNP = cb_useUpnp.Checked;pref.ListeningPort = Int32.Parse(tb_listeningPort.Text);var response = await client.SetPreferences(pref);

If I go into the Debugger I can see that UseUPNP is set to true. The Request that is build also contains this parameter. But if I look at the response (which should be a list of the new set preferences) the useupnp parameter is now false (or 0 in the response context). I think it only affects values of the type Boolean because for example "deviceName" is saved correctly.

I don't know what exactly could be the cause of this.

 

Do you have the same problem? Could this have something to do with the Json converter?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.