libssh
0.11.0
The SSH library
|
NOTE : Please read Chapter 5: The SFTP subsystem before reading this page. The synchronous sftp_read() and sftp_write() have been described there.
SFTP AIO stands for "SFTP Asynchronous Input/Output". This API contains functions which perform async read/write operations on remote files.
File transfers performed using the asynchronous sftp aio API can be significantly faster than the file transfers performed using the synchronous sftp read/write API (see sftp_read() and sftp_write()).
The sftp aio API functions are divided into two categories :
Conceptually, you can think of the sftp aio handle as a request identifier.
Technically, the sftp_aio_begin_*() functions dynamically allocate memory to store information about the i/o request they send and provide the caller a handle to this memory, we call this handle an sftp aio handle.
sftp_aio_wait_*() functions use the information stored in that memory (handled by the caller supplied sftp aio handle) to identify a request, and then they wait for that request's response. These functions also release the memory handled by the caller supplied sftp aio handle (except when they return SSH_AGAIN).
sftp_aio_free() can also be used to release the memory handled by an sftp aio handle but unlike the sftp_aio_wait_*() functions, it doesn't wait for a response. This should be used to release the memory corresponding to an sftp aio handle when some failure occurs. An example has been provided at the end of this page to show the usage of sftp_aio_free().
To begin with, this tutorial will provide basic examples that describe the usage of sftp aio API to perform a single read/write operation.
The later sections describe the usage of the sftp aio API to obtain faster file transfers as compared to the transfers performed using the synchronous sftp read/write API.
On encountering an error, the sftp aio API functions set the sftp and ssh errors just like any other libssh sftp API function. These errors can be obtained using sftp_get_error(), ssh_get_error() and ssh_get_error_code(). The code examples provided on this page ignore error handling for the sake of brevity.
For performing an async read operation on a sftp file (see sftp_open()), the first step is to call sftp_aio_begin_read() to send a read request to the server. The caller is provided an sftp aio handle corresponding to the sent read request.
The second step is to pass a pointer to this aio handle to sftp_aio_wait_read(), this function waits for the server response which indicates the success/failure of the read request. On success, the response indicates EOF or contains the data read from the sftp file.
The following code example shows how a read operation can be performed on an sftp file using the sftp aio API.
For performing an async write operation on a sftp file (see sftp_open()), the first step is to call sftp_aio_begin_write() to send a write request to the server. The caller is provided an sftp aio handle corresponding to the sent write request.
The second step is to pass a pointer to this aio handle to sftp_aio_wait_write(), this function waits for the server response which indicates the success/failure of the write request.
The following code example shows how a write operation can be performed on an sftp file using the sftp aio API.
The above examples were provided to introduce the sftp aio API. This is not how the sftp aio API is intended to be used, because the above usage offers no advantage over the synchronous sftp read/write API which does the same thing i.e issue a request and then immediately wait for its response.
The facility that the sftp aio API provides is that the user can do anything between issuing a request and getting the corresponding response. Any number of operations can be performed after calling sftp_aio_begin_*() [which issues a request] and before calling sftp_aio_wait_*() [which waits for a response]
The code can leverage this feature by calling sftp_aio_begin_*() multiple times to issue multiple requests before calling sftp_aio_wait_*() to wait for the response of an earlier issued request. This approach will keep a certain number of requests outstanding at the client side.
After issuing those requests, while the client code does something else (for example waiting for an outstanding request's response, processing an obtained response, issuing another request or any other operation the client wants to perform), at the same time :
Clearly in this case, operations that the client performs and operations involved in transfer/processing of a outstanding request can occur in parallel. Also, operations involved in transfer/processing of two or more outstanding requests may also occur in parallel (for example when one request travels to the server, another request's response may be incoming towards the client). Such kind of parallelism makes the overall transfer faster as compared to a transfer performed using the synchronous sftp read/write API.
When the synchronous sftp read/write API is used to perform a transfer, a strict sequence is followed:
A file transfer performed in this manner would be slower than the case where multiple read/write requests are kept outstanding at the client side. Because here at any given time, operations related to transfer/processing of only one request/response pair occurs. This is in contrast to the multiple outstanding requests scenario where operations related to transfer/processing of multiple request/response pairs may occur at the same time.
Although it's true that keeping multiple requests outstanding can speed up a transfer, those outstanding requests come at a cost of increased memory consumption both at the client side and the server side. Hence care must be taken to use a reasonable limit for the number of requests kept outstanding.
The further sections provide code examples to show how uploads/downloads can be performed using the sftp aio API and the concept of outstanding requests discussed in this section. In those code examples, error handling has been ignored and at some places pseudo code has been used for the sake of brevity.
The complete code for performing uploads/downloads using the sftp aio API, can be found at https://gitlab.com/libssh/libssh-mirror/-/tree/master.
Before the code examples for uploads and downloads, its important to know about the capping applied by the sftp aio API.
sftp_aio_begin_read() caps the number of bytes the caller can request to read from the remote file. That cap is the value of the max_read_length field of the sftp_limits_t returned by sftp_limits(). Say that cap is LIM and the caller passes x as the number of bytes to read to sftp_aio_begin_read(), then (assuming no error occurs) :
Hence to request server to read x bytes (> LIM), the caller would have to call sftp_aio_begin_read() multiple times, typically in a loop and break out of the loop when the summation of return values of the multiple sftp_aio_begin_read() calls becomes equal to x.
For the sake of simplicity, the code example for download in the upcoming section would always ask sftp_aio_begin_read() to read x <= LIM bytes, so that its return value is guaranteed to be x, unless an error occurs.
Similarly, sftp_aio_begin_write() caps the number of bytes the caller can request to write to the remote file. That cap is the value of max_write_length field of the sftp_limits_t returned by sftp_limits(). Say that cap is LIM and the caller passes x as the number of bytes to write to sftp_aio_begin_write(), then (assuming no error occurs) :
Hence to request server to write x bytes (> LIM), the caller would have to call sftp_aio_begin_write() multiple times, typically in a loop and break out of the loop when the summation of return values of the multiple sftp_aio_begin_write() calls becomes equal to x.
For the sake of simplicity, the code example for upload in the upcoming section would always ask sftp_aio_begin_write() to write x <= LIM bytes, so that its return value is guaranteed to be x, unless an error occurs.
Terminologies used in the following code snippets :
First, we issue the read requests while ensuring that their count doesn't exceed a particular limit decided by us, and the number of bytes requested don't exceed the size of the file to download.
At this point, at max in_flight_requests number of requests may be outstanding. Now we wait for the response corresponding to the earliest issued outstanding request.
On getting that response, we issue another read request if there are still some bytes in the sftp file (to download) for which we haven't sent the read request. (This happens when total_bytes_requested < file_size)
This issuing of another read request (under a condition) is done to keep the number of outstanding requests equal to the value of the in_flight_requests variable.
This process has to be repeated for every remaining outstanding request.
After exiting the while (the queue is not empty) loop, the download would've been complete (assuming no error occurs).
Terminologies used in the following code snippets :
First, we issue the write requests while ensuring that their count doesn't exceed a particular limit decided by us, and the number of bytes requested to write don't exceed the size of the file to upload.
At this point, at max in_flight_requests number of requests may be outstanding. Now we wait for the response corresponding to the earliest issued outstanding request.
On getting that response, we issue another write request if there are still some bytes in the local file (to upload) for which we haven't sent the write request. (This happens when total_bytes_requested < file_size)
This issuing of another write request (under a condition) is done to keep the number of outstanding requests equal to the value of the in_flight_requests variable.
This process has to be repeated for every remaining outstanding request.
After exiting the while (the queue is not empty) loop, the upload would've been complete (assuming no error occurs).
The purpose of sftp_aio_free() was discussed at the beginning of this page, the following code example shows how it can be used during cleanup.