阅读量:3
建立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
拷贝密钥
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; }