B4A Library Tiny FTP Server as Class

Hello,
I created a tiny FTP server implemented as a class to use in B4A apps. The source and an example project is attached.

Why ???

Several of my projects allow the user to add files containing a word list or database records to the app's directory. These are imported and used within the app. Currently I tell people install a third-party FTP, connect using a client on a PC, navigate to my app's directory, and transfer the file. That seems to be a little too much to ask.

Embedding the tiny FTP server into an app allows the user to invoke the FTP using a button or menu item within the app, connect from a PC, and transfer the file directly to the proper directory. Maybe this will be a little easier.

The tiny FTP server is limited in that you cannot change your working directory and I have not implemented all the FTP commands.

At the bottom of the TinyFTPSvr.bas file there are a couple handy utilities: Sprintf - similar to the C function, and FormatDateTime - similar to the Delphi function.

For convenience I have included in the attached zip file the set of utilities, DateUtils.bas, created by Erel.

Barry.
 

Attachments

  • MyFTP.zip
    38.5 KB · Views: 635
Last edited:

canalrun

Well-Known Member
Licensed User
Longtime User
You can just copy and paste the link above on browser..it won't work if u just click it. Thanks

Hello,
Everybody is more than willing to help. I don't think we have enough information to recognize what the problem may be. An image would be very helpful.

The first images didn't show up, the links you posted didn't work, and copying the links into a browser did not work for me, at least. There is a button just below where you enter your reply text, "Upload a File". Press this button, a dialog will open allowing you to search for and upload a file. One of formats allowed is JPG. You will be able to see your uploaded files in an area below the "Upload a File" button to verify that it worked.

Barry.

(I just uploaded an image file)
 

Attachments

  • crunguy.png
    crunguy.png
    937 bytes · Views: 216

RaymondHung

Member
Licensed User
Longtime User
Thank you for help.
 

Attachments

  • CAM00619s.jpg
    CAM00619s.jpg
    312.6 KB · Views: 235
  • CAM00627s.jpg
    CAM00627s.jpg
    270.4 KB · Views: 229

canalrun

Well-Known Member
Licensed User
Longtime User
A picture is worth 1000 words!

I am no movie image format expert. Rather than calling those gridlines, I would call that pixelation or corruption (and that is probably not the correct term either). I would also agree that it is probably caused by data corruption or lost data during the transfer.

What is causing it is a tougher question. During my testing I transferred several large binary files, did a file comparison of the source and destination file, they were identical. But, I was using a fairly fast device, 1 GHz or greater.

A data overrun might cause the error – a new buffer being declared ready before the previous buffer is read. In that case decreasing the tmrData time period may help. I will see if I can find anything that may cause data loss. The code that deals with data connections and transfers is probably the place to look.

Barry.
 

RaymondHung

Member
Licensed User
Longtime User
A picture is worth 1000 words!

I am no movie image format expert. Rather than calling those gridlines, I would call that pixelation or corruption (and that is probably not the correct term either). I would also agree that it is probably caused by data corruption or lost data during the transfer.

What is causing it is a tougher question. During my testing I transferred several large binary files, did a file comparison of the source and destination file, they were identical. But, I was using a fairly fast device, 1 GHz or greater.

A data overrun might cause the error – a new buffer being declared ready before the previous buffer is read. In that case decreasing the tmrData time period may help. I will see if I can find anything that may cause data loss. The code that deals with data connections and transfers is probably the place to look.

Barry.


Thank you very much for your help, waiting for your good news.
 

canalrun

Well-Known Member
Licensed User
Longtime User
I looked at the code, did a bunch of test transfers, and added a little debugging code.

When you are transferring a file using an FTP client, say from a PC to a device, the area of code that deals with transfer is the STOR command. In TinyFTPSvr.bas the code is between lines numbered 447 to 493.

When new data is received from the PC the event AdataStm_NewData() is fired. This subroutine just copies the data bytes from the input stream (FTP socket connected to the PC client) to the output stream (file on the device).

I added some debug code to count the number of bytes transferred.

I transferred several files, small PNG images to a 208 MB movie file. I then compared the number of bytes transferred (this will be displayed in the B4A Log window similar to what's shown below) with the actual file size reported by the PC (note: you must right-click the file, select properties, and then view the actual number of data bytes in the file. See the image attached to this post).

B4X:
Data Socket Connected
STOR debug buffer xfers
STOR buffer total=213750831

In all my tests the correct number of data bytes were transferred. I copied the files back to the PC using a different, third-party FTP server. The files were identical to the original files on the PC.

You may want to try the same test. Run my demo compiled in debug mode. Copy files from the PC to your device using Tiny FTP. Verify that the actual file size on the PC matches the number of bytes copied as shown in the log “STOR buffer total=xxx”. Copy the files back from your device to the PC using some other method (different FTP, ADB, USB,…) to a junk directory and verify that the files are identical to the original PC files.

Barry.

Props.png
 

Attachments

  • TinyFTP.zip
    23.8 KB · Views: 240

RaymondHung

Member
Licensed User
Longtime User
Hi Barry

I tried the same test, file size is the same. But i used UltraEdit to compare, it is not the same data.(see the image attached)

Thanks
 

Attachments

  • log.PNG
    log.PNG
    2.9 KB · Views: 215
  • size.PNG
    size.PNG
    46.3 KB · Views: 202
  • compare.PNG
    compare.PNG
    28.9 KB · Views: 195

canalrun

Well-Known Member
Licensed User
Longtime User
For a transfer to the device (STOR command) all the work is done in the small event handler AdataStm_NewData(). This handler transfers bytes from the input stream to the output stream. Since the correct number of bytes is being reported, somewhere between the input stream and output stream the data is being corrupted. The only way I can think of checking this is by calculating a checksum. If the checksum matches the checksum of the original file, the write to output stream is the culprit. Otherwise, the read from the input stream would be suspect.

A simple checksum would be just adding up all the bytes. A Long should be plenty big enough to hold this value (10e6 x 255 = 2550000000 = 0x97FDE980. Only 4 bytes).

I've attached a new test version of TinyFTP. There are two editions. At the bottom of the initialize subroutine in the TinyFTPSvr class there is a list for filenames. Each file in this list has its checksum calculated. Note the files have to be in the DIirDefaultExternal directory. During a STOR command the checksum is calculated. For both the results are written to the log file. Be aware that calculating a checksum for a large file can take several minutes – it may look like the app is not working at startup.

The way I tested:
  • With a third-party FTP I uploaded a file to the DirDefaultExternal directory of TinyFTP and changed the file name by adding "orig".
  • I added this file to the list of files to have its checksum calculated at start up
  • Run tiny FTP in debug mode.
  • Connect to tiny FTP with the FTP client and upload a file
  • Compare the file size and check some reported for the "orig" file and the uploaded file
In my case both were the same and the actual output file tested to be the same. You will probably find both the same also. If the file sizes are the same, checksums are the same, but the output files end up being different, I think this would point to the output stream write. otherwise it must be the input stream read.

Note that for my tests the checksums came out as negative numbers. This is okay.

From what you have said before I think it may be the output stream write. If that is the case then some other way of writing bytes to the file needs to be investigated.

Barry.
 

Attachments

  • TinyFTP2.zip
    24.1 KB · Views: 242
Last edited:

RaymondHung

Member
Licensed User
Longtime User
Hi Barry

The same results with your ideas, the file sizes are the same, checksums are the same, but the output files end up being different.
Thank you very much to use so much time to help me.
 

Attachments

  • CkSum1.jpg
    CkSum1.jpg
    13.9 KB · Views: 192
  • CkSum2.jpg
    CkSum2.jpg
    12.1 KB · Views: 190

canalrun

Well-Known Member
Licensed User
Longtime User
Given your results, it appears that the data is coming in the input stream properly and been written to the output stream properly, but then something is occurring during the file write that causes data errors.

For the file write I had been using synchronous streams. I modified the TinyFTPSvr to use the ASyncStreams write to the file. I also took out the debug code since that's no longer needed.

Again, testing on my device this worked correctly. See if this helps the problem you are seeing.

Barry.
 

Attachments

  • TinyFTP3.zip
    23.7 KB · Views: 261

RaymondHung

Member
Licensed User
Longtime User
Hi Barry

My test results are "feelgood.mpg (16MB)" normal playback, "Taipei1080p.mp4 (48MB)" can not playback, "iStyle001.jpg (106KB)" and "iStyle002.jpg (96KB)" files can not playback and file size incorrect, the "text.txt (2KB)" file size is 0.

Thanks
 

Attachments

  • Screenshot.jpg
    Screenshot.jpg
    49.2 KB · Views: 201

canalrun

Well-Known Member
Licensed User
Longtime User
That is weird. In all the tests on my devices the transfers complete properly. The devices I used were a Nexus 7 (1.5 GHz I think) and and LG P500h (800 MHz I think).

Transferring a file to the device invokes the STOR command. The work of actually transferring data takes place in the AdataStm_NewData() event handler. This event handler just reads bytes from the input stream and writes the bytes to the output stream. From our tests, on your device, it's getting the correct number of bytes from the input stream. The simple checksum says the data is correct. The data is written to the output stream. But, the resulting file is incorrect. From the pictures you posted earlier it looks like the middle part of the data is in error, not just the beginning or end missing – that would point to the file not being opened or closed properly.

We tried synchronous writes to the output stream and asynchronous writes to the output stream (I believe the asynchronous corresponds to BufferedOutputStreams in Android). We could try creating a large buffer of all the input data and then writing it out the file all at once, but this makes no sense because it would limit the size of transferred files to the amount of available memory.

  • On StackOverflow one post mentions that BufferedOutputStreams are preferred.
  • On my devices TinyFTP works properly.
  • You mentioned a third-party FTP program works properly - yet TinyFTP does not

I am out of ideas. Maybe somebody else can offer the solution. Do you have other devices you can test on?

Barry.
 

RaymondHung

Member
Licensed User
Longtime User
Hi Barry,

Thanks for your guidance on the past few day. I found a comprehensive FTP server Eclipse project code online. Unfortunately I am a beginner, unable to convert to B4A Libraries. Do you have any interest to try on that? If yes, i shall send you a copy.
 

canalrun

Well-Known Member
Licensed User
Longtime User
Yes, please. Send a copy.

I have no experience with libraries either. About six months ago Erel created a tool for building libraries. I don't know if this will be applicable. I have been meaning to give it a try. This may be a good opportunity.

Barry.
 

canalrun

Well-Known Member
Licensed User
Longtime User
Hello,
I haven't made any changes since 2014, but the source code was included.

It may be modified to fix any errors that pop-up because of new Android versions or compiler improvements.

Barry.
 

canalrun

Well-Known Member
Licensed User
Longtime User
It should support most FTP clients.

Remember however, that this is a Tiny FTP server. It can be augmented and improved, but it doesn't sport things like changing destination directories and less frequently used FTP commands. At the time I used it to transfer files from a PC to a specific Android directory within my App.

Barry.
 

wonder

Expert
Licensed User
Longtime User
It should be enough! I'll give it a go! Thanks a lot!

========================================

Alright, I'm able to establish a connection (Filezilla), but immediately after I get a "ADataStm error: java.net.SocketException: Socket closed".
The directory listing is shown but I can't upload/download anything.

What can I do?

** Activity (main) Create, isFirst = true **
Ip address: (...)
** Activity (main) Resume **
FTP Server IP: (...)
FTP Port: 2121
Listening ...
Control Socket Connected
AUTH TLS
AUTH SSL
USER (...)
PASS (password not shown)
SYST
FEAT
PWD
TYPE I
PASV
Data Socket Connected
LIST
(ArrayList) [drwxrwxr-x root sdcard_rw 0 2016-02-04 20:15 (...)]
(...)
-rw-rw-r-- 0 root sdcard_rw 3921 Feb 05 06:58 index.html
(...)
ADataStm error: java.net.SocketException: Socket closed
FTP Socket Busy Error

Note: (...) = personal information / files
 
Top