posts - 20 , comments - 57 , trackbacks - 0

Ftp server in Windows Embedded Compact 2013


With the introduction of Windows Embedded Compact 2013 (aka Windows CE 8.0), the good old File Explorer is no longer present. On itself I understand why Microsoft did this - what end-user application needs a File Explorer? - but during DEBUG development, it was/is a helpful tool.
So what are your options as a developer (debugger)?
- Add the Command Shell to your image. Ok if you can type fast (I am not)
- Add the Telnet Server to your image. Ok if you can type fast (I am not)
- Add the Ftp File Server to your image and use that one for Remote File Manager
- Add the Smb File Server to your image. Unfortunally this one is also no longer available under Windows CE 8.0

I use now the Ftp File Server as a replacement for the File Explorer. It is not as powerful as the old File Explorer or Smb File Server, but will do for the most things I need to do (copying files, typically from my development PC to the device and back).
But the Ftp File Server has always been sloooow in copying files back and forth to the device's local file system, especially when you copy files to slow hard disks (like some Compact Flash cards).
Unless you make a few minor changes to the code.
I will show you what you need to change in the Ftp File Server to make it fast enough to be used as a reasonable replacement.

The main problem with the Ftp File Server is located in method CFtpSession::ReceiveFile().
It reserves a small buffer (4Kb) to accept the data from the socket and then copies this data to the hard disk. It repeats this in a loop until the whole file is copied.

  • The first step is to enlarge the buffer.
But this is not enough, the main problem with the implemented approach is that the buffer is not always entirely filled up till the full 4Kb, but to whatever the socket returns in the recv() call. And this is most of the time less than the buffer size you specify. Moreover, if you have a slow hard disk and you copy the partially filled buffer to the hard disk, this turns out to be very ineffective.

  • It is better to choose the buffer size to best fit the ATAPI’s internal buffer size (typically 4Kb or 64Kb aligned) and to fill it completely (by continuously reading the socket) before to start the slow copy to the hard disk. Practically I choose the buffer size as 64Kb.

With this approach I could speed up Ftp file transfer to the device with a factor of 5 on some slow compact flash disks. Here are my speed improvement code changes:

const int size = 65536;

       char *_ab = new char [size];

       int inbuffer=0;

       do

       {

              inbuffer=0;

              do

              {

                     dwCount = recv(_sockData, (char *)(_ab+inbuffer), size-inbuffer, 0);

                     inbuffer += dwCount;

//                   DEBUGMSG (ZONE_RW, (TEXT("FTP_ReceiveFile at %d bytes/call. Buffer=%d\r\n"), dwCount, inbuffer));

              }      while ((dwCount != SOCKET_ERROR) && (dwCount > 0) && (size-inbuffer > 0));

              if ((dwCount != SOCKET_ERROR) && ((int)inbuffer > 0))

              {

                     bRet = WriteFile(hFile, _ab, inbuffer, &dwRet, NULL);

                     dwTotalRecv += dwRet;

              }

       }      while((bRet) && (_sockData != INVALID_SOCKET) && (dwCount > 0) && (dwCount != SOCKET_ERROR) && (inbuffer == dwRet));

 

       delete _ab;   _ab = NULL;

 

Note that I dynamically allocate the 64kB buffer. This is because this code dates back from Windows CE 4.x/5.0 when memory was scarse. You might as well create it statically or local on the stack.

The Ftp server by default (as shipped by Microsoft) cannot delete or overwrite read-only files. Therefore I always make sure the Ftp Server is only accessible with proper login credentials (although user and password are transmitted in clear text over the wire => not secure), once you are authenticated as admin (ALLOW_WRITE permission), I think it makes sense to have full write access. I modified the code further up in source with my own code as follows:

       WCHAR szFileCopy[MAX_PATH];

       wcscpy(szFileCopy, szFile);

       wcscat(szFileCopy, L".ftpd.bak");

 

       // Make a .bak file of the existing file in case the copy fails (so we still have a backup of the original file)

       if (bRet && bExists)

       {

              DWORD dwAttributes = GetFileAttributes(szFileCopy);

              dwAttributes &= ~FILE_ATTRIBUTE_READONLY;              // Make file writable prior to deleting it. We have ALLOW_WRITE permission.

              SetFileAttributes(szFileCopy, dwAttributes);

              DeleteFile(szFileCopy);                                // Delete previous copy, if any

              bRet = MoveFile(szFile, szFileCopy);                   // Move current file to copy

       }

 

       // Do the copy

       if (bRet)

       {

              // In case the file exists, we will delete it by removing the read-only flag first

              // Note that when the file         exists, szTempFile != szFile ==> szFile can    be deleted

              // Note that when the file doesn't exist,  szTempFile == szFile ==> szFile cannot be deleted

              if (bExists)

              {

                     DWORD dwAttributes = GetFileAttributes(szFile);

                     dwAttributes &= ~FILE_ATTRIBUTE_READONLY;       // Make file writable prior to deleting it. We have ALLOW_WRITE permission.

                     SetFileAttributes(szFile, dwAttributes);

                     DeleteFile(szFile);                             // Delete current file, there should be not one!

              }

              bRet = MoveFile(szTempFile, szFile);                   // Move temp file to new file

       }

 

       // In case the copy succeeded, we can remove the .bak file

       if (bRet && bExists)

       {

              DWORD dwAttributes = GetFileAttributes(szFileCopy);

              dwAttributes &= ~FILE_ATTRIBUTE_READONLY;              // Make file writable prior to deleting it. We have ALLOW_WRITE permission.

              SetFileAttributes(szFileCopy, dwAttributes);

              DeleteFile(szFileCopy);

       }

 

Note:

  • If you are familiar with building your own Windows CE image, you should know it is not a good idea to make changes in the PUBLIC folder. Instead make a copy, typically in your PLATFORM folder, make it buildable and do your changes there.
  • I also used to patch the Smb File Server for a few bugs and speed enhancements. Basically I use the same tricks as described for the Ftp File Server. You can quite easily port it from Windows CE 6.0 or 7.0 and add it to your DEBUG image. Maybe I'll blog on that in the future.

 

Print | posted on Sunday, September 1, 2013 9:22 PM | Filed Under [ Windows CE Windows Embedded Compact Embedded RTOS Ftp Microsoft ]

Feedback

No comments posted yet.
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: