3

First of all, I saw an existing question (JCIFS: file retrieval is too slow to be usable), but it was for Java, not Android, and none of the suggested answers worked.

I created a default project for Android SDK 25 (7.1.1) in Android Studio 2.3, linked the library with compile 'jcifs:jcifs:1.3.17', and typed the following simple test code. The result is below the code.

protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    jcifs.Config.setProperty("jcifs.util.loglevel", "3");
    //jcifs.Config.setProperty("jcifs.smb.client.dfs.disabled", "false");
    //jcifs.Config.setProperty("jcifs.resolveOrder", "DNS");

    try
    {
        NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("", ID, PASSWORD);
        final SmbFile smb = new SmbFile("smb://192.168.XX.XX/Share/FileName", auth);

        Thread t = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                Log.d(TAG, "Test Start");

                for(int i = 1000; i<10000; i+=1000)
                    measure(i);

                Log.d(TAG, "Test End");
            }

            private void measure(int bufferSize)
            {
                Log.d(TAG, "=====Buffer: " + bufferSize + "============");
                try
                {
                    byte[] buffer = new byte[bufferSize];
                    int read = 0;
                    InputStream str = smb.getInputStream();
                    long start = System.nanoTime();

                    while(read < 1000000)
                        read += str.read(buffer);

                    long end = System.nanoTime();
                    str.close();
                    float time = (float) ((end - start) / 1000000000d);
                    float speed = (float) read / 1048576 / time;
                    Log.d(TAG, "Time:" + time + ", size =" + read);
                    Log.d(TAG, "Speed =  " + speed + "MB/s");
                }
                catch(IOException exc)
                {
                    exc.printStackTrace();
                }
            }
        });
        t.start();
    }
    catch(Exception exc)
    {
        Log.d(TAG, exc.toString());
    }
}

Result

Test Start
=====Buffer: 1000============
Time:2.210785, size =1000000
Speed =  0.43137363MB/s
=====Buffer: 2000============
Time:1.4158936, size =1000000
Speed =  0.6735495MB/s
=====Buffer: 3000============
Time:1.0556641, size =1002000
Speed =  0.9051948MB/s
=====Buffer: 4000============
Time:0.7543335, size =1000000
Speed =  1.2642609MB/s
=====Buffer: 5000============
Time:3.6557617, size =1000000
Speed =  0.26086885MB/s
=====Buffer: 6000============
Time:3.292389, size =1002000
Speed =  0.2902396MB/s
=====Buffer: 7000============
Time:2.9179688, size =1001000
Speed =  0.32715496MB/s
=====Buffer: 8000============
Time:2.462616, size =1000000
Speed =  0.38726068MB/s
=====Buffer: 9000============
Time:3.9379272, size =1008000
Speed =  0.24411413MB/s
Test End

Read speed is about 0.2MB/s ~ 1.2MB/s. The device is connected to a 150Mbps Wi-Fi, so, theoretically it can achieve above 10MB/s. The SMB server is not slow either. When I copied the file to a laptop, the read speed was about 30MB/s.

Why is this so slow? What should I check? Why is the read speed about 5 times higher (1.2MB/s) if the buffer size is 4000?

By the way, I have tested copying the same file with other commercial apps. File Commander, Asus File Manager showed similary low speed, ES File Explorer showed about 2MB/s, and Solid Explorer showed about 5MB/s. Since I am pretty sure that all of them use JCIFS (albeit perhaps slightly different versions of it), there must be a way to achieve at least 5MB/s as Solid Explorer does.

Christophe Roussy
  • 16,299
  • 4
  • 85
  • 85
Damn Vegetables
  • 11,484
  • 13
  • 80
  • 135

3 Answers3

2

After using WireShark (network analysis tool) on the Windows computer, I have found that no matter which buffer size I set, the read SMB command always gives 4286 bytes to the Windows computer. It seems that SmbFileInputStream.java is using the max buffer size from the server.

But when I saw the packets from Soild Explorer, it was 32768 bytes. So, I decompiled Solid Explorer's APK (it was of course obfuscated), and saw the SmbFileInputStream.java file inside of it (that file belongs to JCIFS). It seems that the developers of Solid Explorer has modified that file, and set a bigger readSize. So, I tried a similar thing. And then I achieved 5MB/s for the same code above.

Since JCIFS comes with LGPL, the fact that Solid Explorer is using a modified JCIFS without disclosing the source code is a violation of JCIFS' licence. But, oh well, it seems a lot of Android app developers ignores licence of the libraries they use anyway. They do not even properly credit the open-source libraries they used.

Damn Vegetables
  • 11,484
  • 13
  • 80
  • 135
0

Did you try with: jcifs.Config.setProperty("jcifs.smb.client.dfs.disabled", "true");

In my case (though Java) it was helpful for slow connection. By default it is false

Only problem for me is that I'm not sure will that property "break" something else which is working fine..

Jokkeri
  • 1,001
  • 1
  • 13
  • 35
  • Yeah, if you look closely, I had already tried that in my original code sample. As I had written, the problem was the small message size, which introduced more number of roundtrips and overheads. – Damn Vegetables Apr 13 '17 at 11:09
0

There is a patch for large buffer size reading:

From inside the README:

This patch adds two SMBs that supposedly improves read and write performance considerably. Unfortunately it's not crystal clear that all implementation properly support the commands. Note that in addition to this patch an '& 0xFFFF' needs to be added in SmbTransport.java:doRecv:~437 to appear as:

int size = Encdec.dec_uint16be( BUF, 2 ) & 0xFFFF;

although this change has been made in 1.2.7.

Not sure if this works with Android, but the solution could be similar.

Christophe Roussy
  • 16,299
  • 4
  • 85
  • 85