- 建立SSH会话并连接远端服务器
- SSH身份验证
- 密码验证
- 密钥验证
- 生成密钥
- 查看密钥
- 拷贝密钥
- 验证密钥是否正确
- SFTP子系统构建
- 传输普通文件
- 递归传输文件夹
- 完整传输小demo
建立SSH会话并连接远端服务器
- target_host:远端主机IP
- target_username:远端主机用户名
ssh_session session;
// 连接SSH会话
session = ssh_new();
if (!session) {
fprintf(stderr, "Failed to create SSH session\n");
return -1;
}
ssh_options_set(session, SSH_OPTIONS_HOST, target_host);
ssh_options_set(session, SSH_OPTIONS_USER, target_username);
//建立连接
if (ssh_connect(session) != SSH_OK) {
fprintf(stderr, "Failed to connect to SSH session: %s\n", ssh_get_error(session));
ssh_free(session);
return -1;
}
ssh_options_set()
函数设置会话的选项。最重要的选项是:
- SSH_OPTIONS_HOST:要连接到的主机的名称
- SSH_OPTIONS_PORT:使用的端口(默认为端口 22)
- SSH_OPTIONS_USER:要连接的系统用户
- SSH_OPTIONS_LOG_VERBOSITY:打印的消息数量
SSH身份验证
密码验证
直接传输密码即可 target_password即密码
if (ssh_userauth_password(session, NULL, target_password) != SSH_AUTH_SUCCESS) {
fprintf(stderr, "Failed to authenticate with password\n");
ssh_disconnect(session);
ssh_free(session);
return -1;
}
密钥验证
生成密钥
- 基于
ED25519
算法,生成密钥对命令如下:
ssh-keygen -t ed25519 -C "<注释内容>"
- 基于
RSA
算法,生成密钥对命令如下:
ssh-keygen -t rsa -C "<注释内容>"
默认回车即可
查看密钥
ED25519 算法
cat ~/.ssh/id_ed25519.pub
RSA 算法
cat ~/.ssh/id_rsa.pub
拷贝密钥
Windows(在WSL或Git Bash下):
cat ~/.ssh/id_ed25519.pub | clip
Mac:
tr -d '\n' < ~/.ssh/id_ed25519.pub | pbcopy
GNU/Linux (requires xclip):
xclip -sel clip < ~/.ssh/id_ed25519.pub
复制到所需传入的服务器端的
~/.ssh
目录的authorized_keys
文件里面
验证密钥是否正确
if (ssh_userauth_publickey_auto(session, NULL, NULL) != SSH_AUTH_SUCCESS) {
fprintf(stderr, "Authentication failed: %s\n", ssh_get_error(session));
ssh_disconnect(session);
ssh_free(session);
return -1;
}
SFTP子系统构建
sftp_session sftp;
// 打开SFTP通道
sftp = sftp_new(session);
if (!sftp) {
fprintf(stderr, "Failed to create SFTP session\n");
ssh_disconnect(session);
ssh_free(session);
return -1;
}
if (sftp_init(sftp) != SSH_OK) {
fprintf(stderr, "Failed to initialize SFTP session\n");
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
return -1;
}
传输普通文件
- local_file_path:本地需传文件路径
- remote_file_path:传送至远端服务器路径
int sftp_normal_upload(sftp_session sftp, const char* local_file_path, const char* remote_file_path)
{
sftp_file file = sftp_open(sftp, remote_file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (!file) {
fprintf(stderr, "Failed to open remote file: %s\n", remote_file_path);
return -1;
}
FILE* local_file = fopen(local_file_path, "rb");
if (!local_file) {
fprintf(stderr, "Failed to open local file: %s\n", local_file_path);
sftp_close(file);
return -1;
}
// 上传文件内容
char buffer[1024];
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, sizeof(buffer), local_file)) > 0) {
sftp_write(file, buffer, bytes_read);
}
fclose(local_file);
sftp_close(file);
}
递归传输文件夹
sftp没有直接的传输文件夹的接口,我们需要递归目录来传输
假设有下面文件夹
+-- file1
+-- B --+
| +-- file2
A
| +-- file3
+-- C --+
+-- file4
打开目录 A -》进入子目录 B -》在 B 中创建文件 file1 -》在 B 中创建 file2 -》离开目录 B -》进入子目录 C -》在 C 语言中创建 file3 -》 在 C 语言中创建 file4 -》离开目录 C -》离开目录 A
本地遍历的时候,远端同时创建
- local_path:本地路径
- remote_path:远端路径
int sftp_recursive_upload(ssh_session session, sftp_session sftp,
const char* local_path, const char* remote_path) {
// 打开本地目录
DIR* local_dir = opendir(local_path);
if (!local_dir) {
fprintf(stderr, "Failed to open local directory: %s\n", local_path);
return -1;
}
// 创建服务器目录
sftp_mkdir(sftp, remote_path, S_IRWXU);
// 遍历本地目录项
struct dirent* entry;
while ((entry = readdir(local_dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue; // Skip "." and ".."
}
// 构造全路径
char local_file_path[1024];
snprintf(local_file_path, sizeof(local_file_path), "%s/%s", local_path, entry->d_name);
char remote_file_path[1024];
snprintf(remote_file_path, sizeof(remote_file_path), "%s/%s", remote_path, entry->d_name);
// 如果本地条目是一个目录,递归上传它
if (entry->d_type == DT_DIR) {
sftp_recursive_upload(session, sftp, local_file_path, remote_file_path);
}
else { // 如果本地条目是一个普通文件,上传它
sftp_normal_upload(sftp, local_file_path, remote_file_path);
}
}
closedir(local_dir);
return 0;
}
完整传输小demo
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
const char* target_host = "xxx.xx.xxx.xxx";
const char* target_username = "AMY";
const char* target_password = "xxxxx";
int sftp_normal_upload(sftp_session sftp, const char* local_file_path, const char* remote_file_path)
{
sftp_file file = sftp_open(sftp, remote_file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (!file) {
fprintf(stderr, "Failed to open remote file: %s\n", remote_file_path);
return -1;
}
FILE* local_file = fopen(local_file_path, "rb");
if (!local_file) {
fprintf(stderr, "Failed to open local file: %s\n", local_file_path);
sftp_close(file);
return -1;
}
// 上传文件内容
char buffer[1024];
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, sizeof(buffer), local_file)) > 0) {
sftp_write(file, buffer, bytes_read);
}
fclose(local_file);
sftp_close(file);
}
int sftp_recursive_upload(ssh_session session, sftp_session sftp, const char* local_path, const char* remote_path) {
// 打开本地目录
DIR* local_dir = opendir(local_path);
if (!local_dir) {
fprintf(stderr, "Failed to open local directory: %s\n", local_path);
return -1;
}
// 创建服务器目录
sftp_mkdir(sftp, remote_path, S_IRWXU);
// 遍历本地目录项
struct dirent* entry;
while ((entry = readdir(local_dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue; // Skip "." and ".."
}
// 构造全路径
char local_file_path[1024];
snprintf(local_file_path, sizeof(local_file_path), "%s/%s", local_path, entry->d_name);
char remote_file_path[1024];
snprintf(remote_file_path, sizeof(remote_file_path), "%s/%s", remote_path, entry->d_name);
// 如果本地条目是一个目录,递归上传它
if (entry->d_type == DT_DIR) {
sftp_recursive_upload(session, sftp, local_file_path, remote_file_path);
}
else { // 如果本地条目是一个普通文件,上传它
sftp_normal_upload(sftp, local_file_path, remote_file_path);
}
}
closedir(local_dir);
return 0;
}
int main() {
ssh_session session;
sftp_session sftp;
// 连接SSH会话
session = ssh_new();
if (!session) {
fprintf(stderr, "Failed to create SSH session\n");
return -1;
}
ssh_options_set(session, SSH_OPTIONS_HOST, target_host);
ssh_options_set(session, SSH_OPTIONS_USER, target_username);
if (ssh_connect(session) != SSH_OK) {
fprintf(stderr, "Failed to connect to SSH session: %s\n", ssh_get_error(session));
ssh_free(session);
return -1;
}
// 身份验证
if (ssh_userauth_password(session, NULL, target_password) != SSH_AUTH_SUCCESS) {
fprintf(stderr, "Failed to authenticate with password\n");
ssh_disconnect(session);
ssh_free(session);
return -1;
}
// 打开SFTP通道
sftp = sftp_new(session);
if (!sftp) {
fprintf(stderr, "Failed to create SFTP session\n");
ssh_disconnect(session);
ssh_free(session);
return -1;
}
if (sftp_init(sftp) != SSH_OK) {
fprintf(stderr, "Failed to initialize SFTP session\n");
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
return -1;
}
// 得到当前 文件/目录 所在路径
char local_dir[1024];
getcwd(local_dir, sizeof(local_dir));
printf("请输入所需传入的文件或目录的名字\n");
int len = strlen(local_dir);
local_dir[len] = '/';
local_dir[len + 1] = '\0';
char filename[100];
scanf("%s", filename);
strcat(local_dir, filename);
struct stat file_stat;
// 获取文件的详细信息
if (stat(local_dir, &file_stat) != 0) {
perror("stat");
return -1;
}
printf("请输入所需传入远端路径\n");
char target_path[1024];
scanf("%s", target_path);
// 判断文件类型
//普通文件
if (S_ISREG(file_stat.st_mode)) {
sftp_normal_upload(sftp, local_dir, target_path);
}
//目录文件
else if (S_ISDIR(file_stat.st_mode)) {
// 递归上传本地目录到远程目录
if (sftp_recursive_upload(session, sftp, local_dir, target_path) != 0) {
fprintf(stderr, "Failed to recursively upload directory\n");
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
return -1;
}
}
else {
printf("%s 既不是普通文件,也不是目录,无法上传\n", local_dir);
}
// 释放
sftp_free(sftp);
ssh_disconnect(session);
ssh_free(session);
return 0;
}