mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-04-19 02:24:03 +03:00
A section has been added to explain the capping applied by the sftp aio API. Also the example codes have been changed such that they expect sftp_aio_begin_*() functions to return an ssize_t indicating the number of bytes it requested the server to read/write. Signed-off-by: Eshan Kelkar <eshankelkar@galorithm.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com>
706 lines
24 KiB
Plaintext
706 lines
24 KiB
Plaintext
/**
|
|
|
|
@page libssh_tutor_sftp_aio Chapter 10: The SFTP asynchronous I/O
|
|
|
|
@section sftp_aio_api The SFTP asynchronous I/O
|
|
|
|
NOTE : Please read @ref libssh_tutor_sftp 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 :
|
|
- sftp_aio_begin_*() [see sftp_aio_begin_read(), sftp_aio_begin_write()]:
|
|
These functions send a request for an i/o operation to the server and
|
|
provide the caller an sftp aio handle corresponding to the sent request.
|
|
|
|
- sftp_aio_wait_*() [see sftp_aio_wait_read(), sftp_aio_wait_write()]:
|
|
These functions wait for the server response corresponding to a previously
|
|
issued request. Which request ? the request corresponding to the sftp aio
|
|
handle supplied by the caller to these functions.
|
|
|
|
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.
|
|
|
|
@subsection sftp_aio_read Using the sftp aio API for reading (a basic example)
|
|
|
|
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.
|
|
|
|
@code
|
|
ssize_t read_chunk(sftp_file file, void *buf, size_t to_read)
|
|
{
|
|
ssize_t bytes_requested, bytes_read;
|
|
|
|
// Variable to store an sftp aio handle
|
|
sftp_aio aio = NULL;
|
|
|
|
// Send a read request to the sftp server
|
|
bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
|
|
if (bytes_requested == SSH_ERROR) {
|
|
// handle error
|
|
}
|
|
|
|
// Here its possible that (bytes_requested < to_read) as specified in
|
|
// the function documentation of sftp_aio_begin_read()
|
|
|
|
// Wait for the response of the read request corresponding to the
|
|
// sftp aio handle stored in the aio variable.
|
|
bytes_read = sftp_aio_wait_read(&aio, buf, to_read);
|
|
if (bytes_read == SSH_ERROR) {
|
|
// handle error
|
|
}
|
|
|
|
return bytes_read;
|
|
}
|
|
@endcode
|
|
|
|
@subsection sftp_aio_write Using the sftp aio API for writing (a basic example)
|
|
|
|
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.
|
|
|
|
@code
|
|
ssize_t write_chunk(sftp_file file, void *buf, size_t to_write)
|
|
{
|
|
ssize_t bytes_requested, bytes_written;
|
|
|
|
// Variable to store an sftp aio handle
|
|
sftp_aio aio = NULL;
|
|
|
|
// Send a write request to the sftp server
|
|
bytes_requested = sftp_aio_begin_write(file, buf, to_write, &aio);
|
|
if (bytes_requested == SSH_ERROR) {
|
|
// handle error
|
|
}
|
|
|
|
// Here its possible that (bytes_requested < to_write) as specified in
|
|
// the function documentation of sftp_aio_begin_write()
|
|
|
|
// Wait for the response of the write request corresponding to
|
|
// the sftp aio handle stored in the aio variable.
|
|
bytes_written = sftp_aio_wait_write(&aio);
|
|
if (bytes_written == SSH_ERROR) {
|
|
// handle error
|
|
}
|
|
|
|
return bytes_written;
|
|
}
|
|
@endcode
|
|
|
|
@subsection sftp_aio_actual_use Using the sftp aio API to speed up a transfer
|
|
|
|
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 :
|
|
|
|
- Some of those outstanding requests may be travelling over the
|
|
network towards the server.
|
|
|
|
- Some of the outstanding requests may have reached the server and may
|
|
be queued for processing at the server side.
|
|
|
|
- Some of the outstanding requests may have been processed and the
|
|
corresponding responses may be travelling over the network towards the
|
|
client.
|
|
|
|
- Some of the responses corresponding to the outstanding requests may
|
|
have already reached the client side.
|
|
|
|
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:
|
|
|
|
- The client issues a single read/write request.
|
|
- Then waits for its response.
|
|
- On obtaining the response, the client processes it.
|
|
- After the processing ends, the client issues the next read/write request.
|
|
|
|
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.
|
|
|
|
- libssh benchmarks for uploads performed using the sftp aio API [See
|
|
tests/benchmarks/bench_sftp.c]
|
|
- libssh benchmarks for downloads performed using the sftp aio API. [See
|
|
tests/benchmarks/bench_sftp.c]
|
|
- libssh sftp ft API code for performing a local to remote transfer (upload).
|
|
[See src/sftp_ft.c]
|
|
- libssh sftp ft API code for performing a remote to local transfer
|
|
(download). [See src/sftp_ft.c]
|
|
|
|
@subsection sftp_aio_cap Capping applied by the sftp aio API
|
|
|
|
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) :
|
|
|
|
- if x <= LIM, then sftp_aio_begin_read() will request the server
|
|
to read x bytes from the remote file, and will return x.
|
|
|
|
- if x > LIM, then sftp_aio_begin_read() will request the server
|
|
to read LIM bytes from the remote file and will return LIM.
|
|
|
|
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) :
|
|
|
|
- if x <= LIM, then sftp_aio_begin_write() will request the server
|
|
to write x bytes to the remote file, and will return x.
|
|
|
|
- if x > LIM, then sftp_aio_begin_write() will request the server
|
|
to write LIM bytes to the remote file and will return LIM.
|
|
|
|
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.
|
|
|
|
@subsection sftp_aio_download_example Performing a download using the sftp aio API
|
|
|
|
Terminologies used in the following code snippets :
|
|
|
|
- sftp : The sftp_session opened using sftp_new() and initialised using
|
|
sftp_init()
|
|
|
|
- file : The sftp file handle of the remote file to download data
|
|
from. (See sftp_open())
|
|
|
|
- file_size : the size of the sftp file to download. This size can be obtained
|
|
by statting the remote file to download (e.g by using sftp_stat())
|
|
|
|
- We will need to maintain a queue which will be used to store the sftp aio
|
|
handles corresponding to the outstanding requests.
|
|
|
|
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.
|
|
|
|
@code
|
|
sftp_aio aio = NULL;
|
|
|
|
// Chunk size to use for the transfer
|
|
size_t chunk_size;
|
|
|
|
// For the limits structure that would be used
|
|
// by the code to set the chunk size
|
|
sftp_limits_t lim = NULL;
|
|
|
|
// Max number of requests to keep outstanding at a time
|
|
size_t in_flight_requests = 5;
|
|
|
|
// Number of bytes for which requests have been sent
|
|
size_t total_bytes_requested = 0;
|
|
|
|
// Number of bytes which have been downloaded
|
|
size_t bytes_downloaded = 0;
|
|
|
|
// Buffer to use for the download
|
|
char *buffer = NULL;
|
|
|
|
// Helper variables
|
|
size_t to_read;
|
|
ssize_t bytes_requested;
|
|
|
|
// Get the sftp limits
|
|
lim = sftp_limits(sftp);
|
|
if (lim == NULL) {
|
|
// handle error
|
|
}
|
|
|
|
// Set the chunk size for download = the max limit for reading
|
|
// The reason for this has been given in the "Capping applied by
|
|
// the sftp aio API" section (Its to make the code simpler)
|
|
//
|
|
// Assigning a size_t type variable a uint64_t type value here,
|
|
// theoretically could cause an overflow, but practically
|
|
// max_read_length would never exceed SIZE_MAX so its okay.
|
|
chunk_size = lim->max_read_length;
|
|
|
|
buffer = malloc(chunk_size);
|
|
if (buffer == NULL) {
|
|
// handle error
|
|
}
|
|
|
|
... // Code to open the remote file (to download) using sftp_open().
|
|
... // Code to stat the remote file's file size.
|
|
... // Code to open the local file in which downloaded data is to be stored.
|
|
... // Code to initialize the queue which will be used to store sftp aio
|
|
// handles.
|
|
|
|
for (i = 0;
|
|
i < in_flight_requests && total_bytes_requested < file_size;
|
|
++i) {
|
|
to_read = file_size - total_bytes_requested;
|
|
if (to_read > chunk_size) {
|
|
to_read = chunk_size;
|
|
}
|
|
|
|
// Issue a read request
|
|
bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
|
|
if (bytes_requested == SSH_ERROR) {
|
|
// handle error
|
|
}
|
|
|
|
if ((size_t)bytes_requested < to_read) {
|
|
// Should not happen for this code, as the to_read is <=
|
|
// max limit for reading (chunk size), so there is no reason
|
|
// for sftp_aio_begin_read() to return a lesser value.
|
|
}
|
|
|
|
total_bytes_requested += (size_t)bytes_requested;
|
|
|
|
// Pseudo code
|
|
ENQUEUE aio in the queue;
|
|
}
|
|
|
|
@endcode
|
|
|
|
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.
|
|
|
|
@code
|
|
while (the queue is not empty) {
|
|
// Pseudo code
|
|
aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
|
|
|
|
// Wait for the response of the request corresponding to the aio
|
|
bytes_read = sftp_aio_wait_read(&aio, buffer, chunk_size);
|
|
if (bytes_read == SSH_ERROR) {
|
|
//handle error
|
|
}
|
|
|
|
bytes_downloaded += bytes_read;
|
|
if (bytes_read != chunk_size && bytes_downloaded != file_size) {
|
|
// A short read encountered on the remote file before reaching EOF,
|
|
// short read before reaching EOF should never happen for the sftp aio
|
|
// API which respects the max limit for reading. This probably
|
|
// indicates a bad server.
|
|
}
|
|
|
|
// Pseudo code
|
|
WRITE bytes_read bytes from the buffer into the local file
|
|
in which downloaded data is to be stored ;
|
|
|
|
if (total_bytes_requested == file_size) {
|
|
// no need to issue more read requests
|
|
continue;
|
|
}
|
|
|
|
// else issue a read request
|
|
to_read = file_size - total_bytes_requested;
|
|
if (to_read > chunk_size) {
|
|
to_read = chunk_size;
|
|
}
|
|
|
|
bytes_requested = sftp_aio_begin_read(file, to_read, &aio);
|
|
if (bytes_requested == SSH_ERROR) {
|
|
// handle error
|
|
}
|
|
|
|
if ((size_t)bytes_requested < to_read) {
|
|
// Should not happen for this code, as the to_read is <=
|
|
// max limit for reading (chunk size), so there is no reason
|
|
// for sftp_aio_begin_read() to return a lesser value.
|
|
}
|
|
|
|
total_bytes_requested += bytes_requested;
|
|
|
|
// Pseudo code
|
|
ENQUEUE aio in the queue;
|
|
}
|
|
|
|
free(buffer);
|
|
sftp_limits_free(lim);
|
|
|
|
... // Code to destroy the queue which was used to store the sftp aio
|
|
// handles.
|
|
@endcode
|
|
|
|
After exiting the while (the queue is not empty) loop, the download
|
|
would've been complete (assuming no error occurs).
|
|
|
|
@subsection sftp_aio_upload_example Performing an upload using the sftp aio API
|
|
|
|
Terminologies used in the following code snippets :
|
|
|
|
- sftp : The sftp_session opened using sftp_new() and initialised using
|
|
sftp_init()
|
|
|
|
- file : The sftp file handle of the remote file in which uploaded data
|
|
is to be stored. (See sftp_open())
|
|
|
|
- file_size : The size of the local file to upload. This size can be
|
|
obtained by statting the local file to upload (e.g by using stat())
|
|
|
|
- We will need maintain a queue which will be used to store the sftp aio
|
|
handles corresponding to the outstanding requests.
|
|
|
|
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.
|
|
|
|
@code
|
|
sftp_aio aio = NULL;
|
|
|
|
// The chunk size to use for the transfer
|
|
size_t chunk_size;
|
|
|
|
// For the limits structure that would be used by
|
|
// the code to set the chunk size
|
|
sftp_limits_t lim = NULL;
|
|
|
|
// Max number of requests to keep outstanding at a time
|
|
size_t in_flight_requests = 5;
|
|
|
|
// Total number of bytes for which write requests have been sent
|
|
size_t total_bytes_requested = 0;
|
|
|
|
// Buffer to use for the upload
|
|
char *buffer = NULL;
|
|
|
|
// Helper variables
|
|
size_t to_write;
|
|
ssize_t bytes_requested;
|
|
|
|
// Get the sftp limits
|
|
lim = sftp_limits(sftp);
|
|
if (lim == NULL) {
|
|
// handle error
|
|
}
|
|
|
|
// Set the chunk size for upload = the max limit for writing.
|
|
// The reason for this has been given in the "Capping applied by
|
|
// the sftp aio API" section (Its to make the code simpler)
|
|
//
|
|
// Assigning a size_t type variable a uint64_t type value here,
|
|
// theoretically could cause an overflow, but practically
|
|
// max_write_length would never exceed SIZE_MAX so its okay.
|
|
chunk_size = lim->max_write_length;
|
|
|
|
buffer = malloc(chunk_size);
|
|
if (buffer == NULL) {
|
|
// handle error
|
|
}
|
|
|
|
... // Code to open the local file (to upload) [e.g using open(), fopen()].
|
|
... // Code to stat the local file's file size [e.g using stat()].
|
|
... // Code to open the remote file in which uploaded data will be stored [see
|
|
// sftp_open()].
|
|
... // Code to initialize the queue which will be used to store sftp aio
|
|
// handles.
|
|
|
|
for (i = 0;
|
|
i < in_flight_requests && total_bytes_requested < file_size;
|
|
++i) {
|
|
to_write = file_size - total_bytes_requested;
|
|
if (to_write > chunk_size) {
|
|
to_write = chunk_size;
|
|
}
|
|
|
|
// Pseudo code
|
|
READ to_write bytes from the local file (to upload) into the buffer;
|
|
|
|
bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
|
|
if (bytes_requested == SSH_ERROR) {
|
|
// handle error
|
|
}
|
|
|
|
if ((size_t)bytes_requested < to_write) {
|
|
// Should not happen for this code, as the to_write is <=
|
|
// max limit for writing (chunk size), so there is no reason
|
|
// for sftp_aio_begin_write() to return a lesser value.
|
|
}
|
|
|
|
total_bytes_requested += (size_t)bytes_requested;
|
|
|
|
// Pseudo code
|
|
ENQUEUE aio in the queue;
|
|
}
|
|
|
|
@endcode
|
|
|
|
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.
|
|
|
|
@code
|
|
while (the queue is not empty) {
|
|
// Pseudo code
|
|
aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
|
|
|
|
// Wait for the response of the request corresponding to the aio
|
|
bytes_written = sftp_aio_wait_write(&aio);
|
|
if (bytes_written == SSH_ERROR) {
|
|
// handle error
|
|
}
|
|
|
|
// sftp_aio_wait_write() won't report a short write, so no need
|
|
// to check for a short write here.
|
|
|
|
if (total_bytes_requested == file_size) {
|
|
// no need to issue more write requests
|
|
continue;
|
|
}
|
|
|
|
// else issue a write request
|
|
to_write = file_size - total_bytes_requested;
|
|
if (to_write > chunk_size) {
|
|
to_write = chunk_size;
|
|
}
|
|
|
|
// Pseudo code
|
|
READ to_write bytes from the local file (to upload) into a buffer;
|
|
|
|
bytes_requested = sftp_aio_begin_write(file, buffer, to_write, &aio);
|
|
if (bytes_requested == SSH_ERROR) {
|
|
// handle error
|
|
}
|
|
|
|
if ((size_t)bytes_requested < to_write) {
|
|
// Should not happen for this code, as the to_write is <=
|
|
// max limit for writing (chunk size), so there is no reason
|
|
// for sftp_aio_begin_write() to return a lesser value.
|
|
}
|
|
|
|
total_bytes_requested += (size_t)bytes_requested;
|
|
|
|
// Pseudo code
|
|
ENQUEUE aio in the queue;
|
|
}
|
|
|
|
free(buffer);
|
|
|
|
... // Code to destroy the queue which was used to store the sftp aio
|
|
// handles.
|
|
@endcode
|
|
|
|
After exiting the while (the queue is not empty) loop, the upload
|
|
would've been complete (assuming no error occurs).
|
|
|
|
@subsection sftp_aio_free Example showing the usage of sftp_aio_free()
|
|
|
|
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.
|
|
|
|
@code
|
|
void print_sftp_error(sftp_session sftp)
|
|
{
|
|
if (sftp == NULL) {
|
|
return;
|
|
}
|
|
|
|
fprintf(stderr, "sftp error : %d\n", sftp_get_error(sftp));
|
|
fprintf(stderr, "ssh error : %s\n", ssh_get_error(sftp->session));
|
|
}
|
|
|
|
// Returns 0 on success, -1 on error
|
|
int write_strings(sftp_file file)
|
|
{
|
|
const char * strings[] = {
|
|
"This is the first string",
|
|
"This is the second string",
|
|
"This is the third string",
|
|
"This is the fourth string"
|
|
};
|
|
|
|
size_t string_count = sizeof(strings) / sizeof(strings[0]);
|
|
size_t i;
|
|
|
|
sftp_session sftp = NULL;
|
|
sftp_aio aio = NULL;
|
|
|
|
int rc;
|
|
|
|
if (file == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
... // Code to initialize the queue which will be used to store sftp aio
|
|
// handles
|
|
|
|
sftp = file->sftp;
|
|
for (i = 0; i < string_count; ++i) {
|
|
rc = sftp_aio_begin_write(file,
|
|
strings[i],
|
|
strlen(strings[i]),
|
|
&aio);
|
|
if (rc == SSH_ERROR) {
|
|
print_sftp_error(sftp);
|
|
goto err;
|
|
}
|
|
|
|
// Pseudo code
|
|
ENQUEUE aio in the queue of sftp aio handles
|
|
}
|
|
|
|
for (i = 0; i < string_count; ++i) {
|
|
// Pseudo code
|
|
aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
|
|
|
|
rc = sftp_aio_wait_write(&aio);
|
|
if (rc == SSH_ERROR) {
|
|
print_sftp_error(sftp);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
|
|
... // Code to destroy the queue in which sftp aio handles were
|
|
// stored
|
|
|
|
return 0;
|
|
|
|
err:
|
|
|
|
while (queue is not empty) {
|
|
// Pseudo code
|
|
aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
|
|
|
|
sftp_aio_free(aio);
|
|
}
|
|
|
|
... // Code to destroy the queue in which sftp aio handles were
|
|
// stored.
|
|
|
|
return -1;
|
|
}
|
|
|
|
@endcode
|
|
|
|
*/
|