├── README.md ├── cacert.pem ├── client ├── client.c ├── common.c ├── common.h ├── login.c ├── login.h ├── make_ca_pk.sh ├── mk.sh ├── privkey.pem ├── pthread_pool.c ├── pthread_pool.h ├── server ├── server.c └── test ├── client └── client └── server ├── cacert.pem ├── privkey.pem └── server /README.md: -------------------------------------------------------------------------------- 1 | # File-Transfer-System 2 | 网络文件安全传输系统,其中利用TCP协议进行文件传输,SSL技术进行加密,同时结合线程池实现多台客户机同时向服务机上传或下载文件的功能。 3 | 4 | > 本系统参考了[dayL_W](https://blog.csdn.net/u013181595)的文章完成:[网络安全传输系统](https://so.csdn.net/so/search/s.do?q=%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E4%BC%A0%E8%BE%93%E7%B3%BB%E7%BB%9F&t=blog&u=u013181595)。 5 | 6 | ### 具体功能 7 | 8 | - 基于C/S模型(Client/Server),实现文件安全的上传和下载功能; 9 | - 利用TCP协议进行文件的传输; 10 | - 通过OpenSSL技术对TCP包的明文数据进行加密再传输; 11 | - 采用线程池技术,实现多个客户机同时访问服务器的功能; 12 | - 采用MySQL数据库实现客户端的账号管理功能。 13 | 14 | ### 使用方法 15 | 16 | - 本系统需要先安装的环境有: 17 | 18 | 书籍Linux/Unix系统编程手册的API接口函数[https://www.cnblogs.com/pluse/p/6296992.html](https://www.cnblogs.com/pluse/p/6296992.html) 19 | 20 | MYSQL以及OpenSSL库 21 | 22 | - 第一次使用还要在服务端创建一个数据库 23 | 24 | 数据库学的不深,学完在回来改。具体过程: 25 | 26 | ```sql 27 | -- 创建数据库 project_ssl_login 28 | CREATE DATABASE project_ssl_login character set utf8; 29 | -- 选择数据库 30 | use project_ssl_login; 31 | -- 创建一个数据表 login 32 | CREATE TABLE login( 33 | username VARCHAR(20), 34 | password VARCHAR(20), 35 | unique(username) 36 | ); 37 | ``` 38 | 39 | - 编译文件 40 | 41 | ```bash 42 | # 服务端 43 | gcc server.c common.c pthread_pool.c login.c -o server -ltlpi -lssl -lcrypto -ldl -lpthread -I/usr/include/mysql/ -lmysqlclient -Wall -g 44 | # 客户端 45 | gcc client.c common.c pthread_pool.c login.c -o client -ltlpi -lssl -lcrypto -ldl -lpthread -I/usr/include/mysql/ -lmysqlclient -Wall -g 46 | ``` 47 | 48 | - 服务端生成公钥和私钥 49 | 50 | ```bash 51 | # 产生私钥 52 | openssl genrsa -out privkey.pem 2048 53 | # 产生公钥 54 | openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095 55 | ``` 56 | 57 | - 可以愉快的运行使用啦 58 | 59 | ```bash 60 | # 服务端 61 | ./server cacert.pem privkey.pem 62 | # 客户端 63 | ./client ip地址 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDoDCCAoigAwIBAgIJAIKF8LblCfBqMA0GCSqGSIb3DQEBCwUAMGUxETAPBgNV 3 | BAcMCHNoZW56aGFuMQwwCgYDVQQKDANzenUxDDAKBgNVBAsMA3N6eTENMAsGA1UE 4 | AwwEc3N6dTElMCMGCSqGSIb3DQEJARYWeWtodWFuZy5pbml0QGdtYWlsLmNvbTAe 5 | Fw0xOTEwMjYwOTMwMzBaFw0yMjEwMjUwOTMwMzBaMGUxETAPBgNVBAcMCHNoZW56 6 | aGFuMQwwCgYDVQQKDANzenUxDDAKBgNVBAsMA3N6eTENMAsGA1UEAwwEc3N6dTEl 7 | MCMGCSqGSIb3DQEJARYWeWtodWFuZy5pbml0QGdtYWlsLmNvbTCCASIwDQYJKoZI 8 | hvcNAQEBBQADggEPADCCAQoCggEBALCVnv1AYBv3AN73q8Abb6751jNrpx8YKNbH 9 | yeBbgg/vc0XR6g90hOEsqHUDfG1ndTPMeApQr8jR+7pxRdrKNAtfwemDXlOtKdJ6 10 | DhLmKSUTfm3+OEWzGQLlHt+86/5UCZpDn335T+prYSNvvj49fkUySQF1RDNXaYVX 11 | OWuP7VwNt4hcRfBf2CyFvvf3YQOLI9mc/5+wX/+UqeowHF5TANqf7vnk+aKw9Ve3 12 | W/NwmyGfawu1scKfJlXMz4wfx4v1EIpSvmGS7cCWpZTvtTQrnRBKxzj57KmYI/f+ 13 | WY6/NZ5AfPXWVPGp6IOwOyi+wZzXyIfHfAdMrzHab0DuV3HTd9sCAwEAAaNTMFEw 14 | HQYDVR0OBBYEFDpG8umxbkbHAMFSyt6GQrGU9Y+pMB8GA1UdIwQYMBaAFDpG8umx 15 | bkbHAMFSyt6GQrGU9Y+pMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD 16 | ggEBAD/YKDhPTS3idhjbzQ/y+SCmTD/Bqwben42q7hYDKGVtl5LDkGeMspT1yIAe 17 | NGPa5VFlEj0KEvv4CzV0eToXv00KN7bdRD2jomvk9qx9f1iCBxHXthuJ1RFyfrjd 18 | HOU+oLmskwU3XRraWWnkWVo4ob+/76Z9Hio0p+iyduz9fOM9WkhdLKVWdXMFilM6 19 | 15lccYFBqpJcjmfOHSSkhwp6QA22dbaPJi9xQnsndBazf1u7Cjb3pJvusU0G75yc 20 | oAjmKcuZUUM5SP4BilQRHZ5dZejiF5KGcKpEprF81ecyKIjfZ4BJ8BAtzItS1xhC 21 | DQahfT4MukUCF+0h84EEUFgf9w4= 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /client: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coder-21Hertz/File-Transfer-System/0b5c984cc2ca3fa1519d8b3d8c6aa75384237fe6/client -------------------------------------------------------------------------------- /client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 客户端程序 3 | * @Company: SZU 4 | * @Author: PerkyRookie 5 | * @Date: 2019-04-08 14:31:56 6 | * @LastEditors: PerkyRookie 7 | * @LastEditTime: 2019-07-11 16:07:31 8 | */ 9 | 10 | #include "login.h" 11 | #include "common.h" 12 | 13 | 14 | int main(int argc, char *argv[]) 15 | { 16 | if(argc != 2 || strcmp(argv[1], "--help") == 0) 17 | usageErr("%s IP_Address\n", argv[0]); 18 | setbuf(stdout,NULL); //关闭缓冲 19 | 20 | SSL_library_init(); 21 | OpenSSL_add_all_algorithms(); 22 | SSL_load_error_strings(); 23 | SSL_CTX *ctx=SSL_CTX_new(SSLv23_client_method()); 24 | if(ctx==NULL) 25 | errExit("SSL_CTX_new"); 26 | 27 | int clientfd=socket(AF_INET,SOCK_STREAM,0); 28 | if(clientfd == -1) 29 | errExit("socket"); 30 | 31 | /* IPv4的socket 地址 */ 32 | struct sockaddr_in svraddr; 33 | memset(&svraddr, 0, sizeof(svraddr)); 34 | /* 取出ip地址 */ 35 | if(inet_pton(AF_INET, argv[1], &svraddr.sin_addr)==0) 36 | errExit("inet_pton"); 37 | svraddr.sin_family=AF_INET; 38 | svraddr.sin_port=htons(PORT); 39 | 40 | if(connect(clientfd, (struct sockaddr*)&svraddr, sizeof(svraddr)) == -1) 41 | errExit("connect"); 42 | 43 | SSL *ssl=SSL_new(ctx); 44 | SSL_set_fd(ssl,clientfd); 45 | if(SSL_connect(ssl) == -1) 46 | errExit("SSL_connect"); 47 | /* 输出证书信息 */ 48 | ShowCerts(ssl); 49 | 50 | /* 登录操作 */ 51 | if(client_login(ssl) == 3) 52 | { 53 | close(clientfd); 54 | SSL_CTX_free(ctx); 55 | return 0; 56 | } 57 | 58 | char cmd[BUFFSIZE]; 59 | while(1) 60 | { 61 | memset(cmd, 0, BUFFSIZE); 62 | printf("请输入您要操作和文件(recv/send+file): "); 63 | scanf("%s",cmd); 64 | getchar(); //去除末尾回车符 65 | 66 | /* 其他字符,不发送 */ 67 | if(*cmd != 'r' && *cmd != 's' && *cmd != 'q') 68 | { 69 | printf("无效的输入命令\n"); 70 | continue; 71 | } 72 | 73 | /* 发送命令行输入信息 */ 74 | socklen_t len = SSL_write(ssl, cmd, strlen(cmd)); 75 | if (len <= 0) 76 | errExit("SSL_write"); 77 | 78 | if(*cmd == 'q') 79 | { 80 | SSL_shutdown(ssl); 81 | SSL_free(ssl); 82 | break; 83 | } 84 | 85 | /* 分割命令行输入信息 */ 86 | char filename[FILE_NAME_MAX_SIZE]; 87 | char operate = my_strtok(cmd, filename); 88 | 89 | /* 从服务器读取文件 */ 90 | if(operate == 'r') 91 | { 92 | recv_file(filename, ssl, argv[1]); 93 | } 94 | /* 发送文件到服务器 */ 95 | else if(operate == 's') 96 | { 97 | send_file(filename, ssl, argv[1]); 98 | } 99 | 100 | } 101 | close(clientfd); 102 | SSL_CTX_free(ctx); 103 | return 0; 104 | } -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 服务器和客户端共用头文件 3 | * @Company: SZU 4 | * @Author: PerkyRookie 5 | * @Date: 2019-04-09 09:22:49 6 | * @LastEditors: PerkyRookie 7 | * @LastEditTime: 2019-07-23 14:57:57 8 | * @Compilation: gcc server.c common.c pthread_pool.c login.c -o server -ltlpi -lssl -lcrypto -ldl -lpthread -I/usr/include/mysql/ -lmysqlclient -Wall -g 9 | * @Compilation: gcc client.c common.c pthread_pool.c login.c -o client -ltlpi -lssl -lcrypto -ldl -lpthread -I/usr/include/mysql/ -lmysqlclient -Wall -g 10 | * @running: ./server cacert.pem privkey.pem ./client ip地址 11 | */ 12 | 13 | #include "common.h" 14 | 15 | /* 16 | * @description: 用于分割命令行输入的操作命令,分隔符为"+" 17 | * @param {char *} buff: 要分割的字符串 18 | * @param {char []} filename: 分割后的第二个字符串 19 | * @return: 返回命令行输入第一个字符 20 | */ 21 | char my_strtok(char *buff, char filename[FILE_NAME_MAX_SIZE]) 22 | { 23 | const char separator[2] = "+"; 24 | memset(filename, 0, FILE_NAME_MAX_SIZE); 25 | /* 获取第一个子字符串(判断发送还是接收) */ 26 | char *operate = strtok(buff, separator); 27 | /* 获取第二个子字符串(文件名) */ 28 | char *filen = strtok(NULL,separator); 29 | /*限制文件名长度 */ 30 | strncpy(filename, filen, strlen(filen)>FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE 31 | : strlen(filen)); 32 | return *operate; 33 | } 34 | 35 | /* 36 | * @description: 发送一个文件 37 | * @param {char *} filename: 要发送的文件 38 | * @param {SSL *} ssl: 建立的ssl连接 39 | * @return: 成功则返回0,出错返回-1 40 | */ 41 | void send_file(char *filename, SSL *ssl, char *claddrStr) 42 | { 43 | FILE *fd=fopen(filename, "r"); 44 | socklen_t len; 45 | if(fd == NULL) 46 | { 47 | printf("File :%s not found!\n", filename); 48 | /* 通知客户端,没有这个文件 */ 49 | len = SSL_write(ssl, "not", strlen("not")); 50 | if (len <= 0) 51 | errExit("send_file.SSL_write.1"); 52 | } 53 | else 54 | { 55 | len = SSL_write(ssl, "yes", strlen("yes")); 56 | if (len <= 0) 57 | errExit("send_file.SSL_write.2"); 58 | 59 | /* 读取并发送文件长度 */ 60 | struct stat fstat; 61 | if((stat(filename,&fstat)) == -1) 62 | errExit("send_file.stat"); 63 | len = SSL_write(ssl,(void *)&fstat.st_size,4); 64 | if (len <= 0) 65 | errExit("send_file.SSL_write.3"); 66 | 67 | printf("准备发送文件: %s 到 %s \n", filename, claddrStr); 68 | char buff[BUFFSIZE]; 69 | memset(buff, 0, BUFFSIZE); 70 | int file_block_length=0; 71 | while((file_block_length = fread(buff, sizeof(char), BUFFSIZE, fd))>0) 72 | { 73 | printf("file_block_length:%d\n",file_block_length); 74 | if(SSL_write(ssl, buff, file_block_length) == -1) 75 | errExit("send_file.SSL_write.4"); 76 | 77 | memset(buff, 0, BUFFSIZE); 78 | } 79 | printf("发送完成!\n"); 80 | fclose(fd); 81 | } 82 | } 83 | 84 | /* 85 | * @description: 读取一个文件 86 | * @param {char *} filename: 要读取的文件 87 | * @param {SSL *} ssl: 建立的ssl连接 88 | * @return: 成功则返回0,出错返回-1 89 | */ 90 | void recv_file(char *filename, SSL *ssl, char *claddrStr) 91 | { 92 | int length=0; 93 | char buff[BUFFSIZE]; 94 | memset(buff, 0, BUFFSIZE); 95 | /* 判断文件是否存在 */ 96 | length=SSL_read(ssl,buff,BUFFSIZE); 97 | if(length <= 0) 98 | errExit("recv_file.SSL_read.1"); 99 | if(*buff == 'n') 100 | { 101 | printf("File :%s not found!\n", filename); 102 | } 103 | else 104 | { 105 | FILE *fd = fopen(filename,"wb+"); //打开或者创建一个文件 106 | if(fd==NULL) 107 | errExit("recv_file.fopen"); 108 | 109 | /* 开始接收文件 */ 110 | char buff[BUFFSIZE]; 111 | memset(buff, 0, BUFFSIZE); 112 | 113 | int filesize=0; 114 | int totalrecv=0; 115 | /* 接收文件长度 */ 116 | length = SSL_read(ssl, &filesize, 4); 117 | if(length <= 0) 118 | errExit("recv_file.SSL_read.2"); 119 | 120 | while((length = SSL_read(ssl,buff,BUFFSIZE))) 121 | { 122 | if(length <= 0) 123 | errExit("recv_file.SSL_read.3"); 124 | if(fwrite(buff, sizeof(char), length, fd) < length) 125 | errExit("recv_file.fwrite"); 126 | /* 匹配文件长度 */ 127 | totalrecv += length; 128 | if(totalrecv == filesize) 129 | break; 130 | 131 | memset(buff, 0, BUFFSIZE); 132 | } 133 | printf("收到文件: %s 来自 %s !\n", filename, claddrStr); 134 | fclose(fd); 135 | } 136 | } 137 | 138 | /* 139 | * @description: socket连接服务端初始化 140 | * @param {type} 无 141 | * @return: 成功则返回0,出错返回-1 142 | */ 143 | int socket_init() 144 | { 145 | //创建 socket 146 | struct sockaddr_in svraddr; 147 | memset(&svraddr, 0, sizeof(svraddr)); 148 | 149 | svraddr.sin_family = AF_INET; 150 | svraddr.sin_addr.s_addr = htonl(INADDR_ANY); 151 | svraddr.sin_port = htons(PORT); 152 | 153 | int sockfd = socket(AF_INET, SOCK_STREAM, 0); 154 | if (sockfd == -1) 155 | errExit("socket_init.socket"); 156 | 157 | /* 防止重启进入TIME_WAIT状态 */ 158 | int reuse = 1; 159 | if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) 160 | errExit("socket_init.setsockopt"); 161 | 162 | if (bind(sockfd, (struct sockaddr *)&svraddr, sizeof(svraddr)) == -1) 163 | errExit("socket_init.bind"); 164 | 165 | if (listen(sockfd, LISTENQ) == -1) 166 | errExit("socket_init.listen"); 167 | 168 | return sockfd; 169 | } 170 | 171 | 172 | /* 173 | * @description: 输出证书信息 174 | * @param {SSL *} ssl: 建立的ssl连接 175 | * @return: 无 176 | */ 177 | void ShowCerts (SSL* ssl) 178 | { 179 | X509 *cert; 180 | char *line; 181 | 182 | cert=SSL_get_peer_certificate(ssl); 183 | if(cert != NULL){ 184 | printf("数字证书信息:\n"); 185 | line=X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); 186 | printf("证书:%s\n", line); 187 | free(line); 188 | line=X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); 189 | printf("颁发者:%s\n", line); 190 | free(line); 191 | X509_free(cert); 192 | }else 193 | printf("无证书信息!\n"); 194 | } -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "tlpi_hdr.h" 11 | 12 | #define PORT 6000 13 | #define LISTENQ 20 14 | #define BUFFSIZE 4096 15 | #define FILE_NAME_MAX_SIZE 512 16 | 17 | char my_strtok(char *buff, char filename[FILE_NAME_MAX_SIZE]); 18 | void send_file(char *filename, SSL *ssl, char *claddrStr); 19 | void recv_file(char *filename, SSL *ssl, char *claddrStr); 20 | int socket_init(); 21 | void ShowCerts (SSL* ssl); 22 | 23 | 24 | #endif // !COMMON_H -------------------------------------------------------------------------------- /login.c: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 账号管理系统 3 | * @Company: SZU 4 | * @Author: PerkyRookie 5 | * @Date: 2019-04-10 14:34:26 6 | * @LastEditors: PerkyRookie 7 | * @LastEditTime: 2019-07-11 20:46:20 8 | */ 9 | 10 | #include "login.h" 11 | 12 | int callback(void *NotUsed, int argc, char *argv[], char **azColName) 13 | { 14 | int i; 15 | for (i = 0; i < argc; i++) 16 | { 17 | strcpy(passwd_d, argv[i]); 18 | } 19 | return 0; 20 | } 21 | 22 | char server_login(SSL *ssl) 23 | { 24 | char username[20]; 25 | char password[20]; 26 | int login_or_create; 27 | char temp[100]; 28 | char buf[10]; 29 | int login_flag = 0; 30 | 31 | /* 检测是否退出 */ 32 | if(SSL_read(ssl, temp, 100) <= 0) 33 | errExit("server_login.SSL_read.1"); 34 | if(*temp == 'q') 35 | return *temp; 36 | 37 | MYSQL *conn_ptr; 38 | MYSQL_RES *res_ptr; 39 | MYSQL_ROW sqlrow; 40 | int res; 41 | char sql[100]; 42 | 43 | conn_ptr = mysql_init(NULL); 44 | if (!conn_ptr) { 45 | printf("mysql_init failed\n"); 46 | return EXIT_FAILURE; 47 | } 48 | //root 为用户名 123456为密码 project_ssl_login为要连接的database 49 | conn_ptr = mysql_real_connect(conn_ptr, "localhost", "root", "123456", "project_ssl_login", 0, NULL, 0); 50 | 51 | do{ 52 | login_flag = 1; 53 | if(SSL_read(ssl, temp, 100) <= 0) 54 | errExit("server_login.SSL_read.2"); 55 | 56 | sscanf(temp, "log:%d username:%s password:%s", &login_or_create, username, password); 57 | 58 | /* 注册 */ 59 | if(login_or_create == 2) 60 | { 61 | sprintf(sql, "insert into login values('%s','%s')",username, password); 62 | res = mysql_query(conn_ptr, sql); 63 | 64 | if(res) 65 | { 66 | printf("用户名存在:username:%s \n",passwd_d); 67 | login_flag = 3; 68 | } 69 | else 70 | { 71 | printf("用户注册:username:%s password:%s\n",username,password); 72 | login_flag = 2; 73 | } 74 | } 75 | //登录 76 | else 77 | { 78 | sprintf(sql, "select password from login where username='%s';",username); 79 | res = mysql_query(conn_ptr, sql); 80 | if (res) { 81 | printf("SELECT error:%s\n",mysql_error(conn_ptr)); 82 | } else { 83 | res_ptr = mysql_store_result(conn_ptr); //取出结果集 84 | if(res_ptr) { 85 | sqlrow = mysql_fetch_row(res_ptr); 86 | printf("%s\n", sqlrow[0]); 87 | if(strcmp(password,sqlrow[0]) == 0) 88 | { 89 | printf("用户登录:username:%s password:%s\n",username,passwd_d); 90 | login_flag = 1; 91 | } 92 | else 93 | { 94 | printf("登录失败:username:%s password:%s\n",username,passwd_d); 95 | login_flag = 0; 96 | } 97 | } 98 | } 99 | } 100 | sprintf(buf,"login:%d",login_flag); 101 | 102 | if(SSL_write(ssl,buf,10) <= 0) 103 | errExit("server_login.SSL_write.1"); 104 | 105 | }while(login_flag == 0 || login_flag == 3); 106 | 107 | mysql_close(conn_ptr); 108 | 109 | return 's'; 110 | } 111 | 112 | int client_login(SSL *ssl) 113 | { 114 | int login_or_create; 115 | int login_flag = 0; 116 | char username[20]; 117 | char password[20]; 118 | char temp[100]; 119 | char buf[10]; 120 | do 121 | { 122 | memset(buf, 0, 10); 123 | do{ 124 | printf("请输入您的选择(1: 登录,2: 注册,3: 退出): "); 125 | scanf("%d", &login_or_create); 126 | getchar(); 127 | }while(login_or_create != 1 && login_or_create != 2 && login_or_create != 3); 128 | 129 | 130 | if(login_or_create == 3){ 131 | if(SSL_write(ssl, "quit", 100) <= 0) 132 | errExit("client_login.SSL_write.1"); 133 | return login_or_create; 134 | } 135 | if(SSL_write(ssl, "continue", 100) <= 0) 136 | errExit("client_login.SSL_write.2"); 137 | 138 | printf("用户名: "); 139 | scanf("%s", username); 140 | getchar(); 141 | printf("密码: "); 142 | scanf("%s", password); 143 | getchar(); 144 | sprintf(temp, "log:%d username:%s password:%s", login_or_create, username, password); 145 | 146 | if(SSL_write(ssl, temp, 100) <= 0) 147 | errExit("client_login.SSL_write.3"); 148 | if(SSL_read(ssl, buf, 10) <= 0) 149 | errExit("client_login.SSL_read.1"); 150 | sscanf(buf, "login:%d", &login_flag); 151 | 152 | if (login_flag == 1) 153 | { 154 | printf("---------登录成功!---------\n"); 155 | } 156 | else if (login_flag == 2) 157 | { 158 | printf("---------注册成功!---------\n"); 159 | } 160 | else if(login_flag == 3) 161 | { 162 | printf("--------用户名已存在!-------\n"); 163 | } 164 | else if(login_flag == 0) 165 | { 166 | printf("------用户名或密码错误!------\n"); 167 | } 168 | } while (login_flag == 0 || login_flag == 3); 169 | 170 | return login_or_create; 171 | } -------------------------------------------------------------------------------- /login.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGIN_H 2 | #define LOGIN_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "tlpi_hdr.h" 8 | 9 | char passwd_d[20]; 10 | 11 | int callback(void *NotUsed, int argc, char *argv[], char **azColName); 12 | char server_login(SSL *ssl); 13 | int client_login(SSL *ssl); 14 | 15 | #endif // !LOGIN_H 16 | -------------------------------------------------------------------------------- /make_ca_pk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | openssl genrsa -out privkey.pem 2048 3 | openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095 -------------------------------------------------------------------------------- /mk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | gcc -o client client.c -lssl -lcrypto -ldl 3 | gcc -o server server.c -lssl -lcrypto -ldl 4 | -------------------------------------------------------------------------------- /privkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAsJWe/UBgG/cA3verwBtvrvnWM2unHxgo1sfJ4FuCD+9zRdHq 3 | D3SE4SyodQN8bWd1M8x4ClCvyNH7unFF2so0C1/B6YNeU60p0noOEuYpJRN+bf44 4 | RbMZAuUe37zr/lQJmkOffflP6mthI2++Pj1+RTJJAXVEM1dphVc5a4/tXA23iFxF 5 | 8F/YLIW+9/dhA4sj2Zz/n7Bf/5Sp6jAcXlMA2p/u+eT5orD1V7db83CbIZ9rC7Wx 6 | wp8mVczPjB/Hi/UQilK+YZLtwJallO+1NCudEErHOPnsqZgj9/5Zjr81nkB89dZU 7 | 8anog7A7KL7BnNfIh8d8B0yvMdpvQO5XcdN32wIDAQABAoIBACGyS4aUNELh3Z6u 8 | sG2WTrebIU7qqhd66sAhm9peW/FihiK6remJGJpHhVXe9m2CIpFh4T5Gq2Ss/VZU 9 | qznXABtziHNhCCz7jFGHzQu6M/vJPoUKz/4s8QbmZf/X80izDKoNYXAOlTtshjrW 10 | 5fzGi0P1Fo1arUsG0TIKtQBblLXN7x1Aqwc/8flRB7VFowjhojUZvg31h2yu/51v 11 | Vx4wWBwWOeaQK2t5vwovrCNTVXMvF3p1EI1vCVRU9Ze6VoHhNict8f9a4RtNhdza 12 | w/0g31Ba8kOtjWeGaoFT45WWYkq7GDFDP4BMM0iGO0f2Nh3XeYFM9C0WaD5SW5af 13 | VpBEVvECgYEA2l8MBD8esGhpyjT8tsGRRbr4eevXC6OhXo0hN4Z4zQ36Xi29pH/e 14 | 2R2/trBKo20QCwTqjXwnGJYFmxxSzOgw5XCrx+o8guT5C8glzXT6pfouL8oxlnqg 15 | cyzcGSy8jnMGMBeU6l/7oSJgDt5AEJpK/BdZu/+q5Ed/oJX3YEfkKnUCgYEAzwM+ 16 | UakrWZsGhfiK5EJ0Ilorw/qaNk37JhlSwv7BtsI9s0DKj+SGz3b0My6baNqVIlay 17 | rf0Hx5ceU9LrMC9raLUZxr55VIAu1VHKgInUD/5xXcak8SlcxY403FXaCursSj2d 18 | 9367Q/edeO1oLDLJPWstz991I/Ie/ssqcbTTrw8CgYAvJD2v3QB/DMtpv66etYLr 19 | DsX9bBBsjtKTWtxE5F7HxbC7DBzYmlI9hNKSWdSDG1RPTKxmBttZz2k+Zwro+Wc0 20 | 4PH0oniVuWCaTob6p7pEgpHd90RlO+vPcWvCc4nxJpu7XhvjzPzBeG5MR2aFLwnp 21 | ChFxKGx4A4dl33ob6uky0QKBgCOcgTn3ChgODR861FnT/Lp0HJdIIwM4gv3Y2V3m 22 | CVjxLUAK403BmFdUNUoguGdfQsx3caFwp8fz/SmaXQ8a+TU4AkkoNyMMMgJMYbOf 23 | AjYGJWZmZ7V80hLCFeHZevrp0uAqnWQYDwrwK8zDumnwCqJgL69U14fpfN+D/7cz 24 | zj6NAoGAa1f4yOhuLi+/FVhut+fyT0b995vrGOxxR53M9DbPIMD+q7G5yhf7U5Ye 25 | hALolwTSh6iQjAzVDn7mRA7fRGhmPFvhi8J2yvx9Qry9W+JDLKJE+mY1vb9h0INO 26 | nNGdrMyT3rHOxBqljiIIn8A0rUqpLMMeoTBackHMmVRNuZpSm40= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /pthread_pool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 线程池 3 | * @Company: SZU 4 | * @Author: PerkyRookie 5 | * @Date: 2019-04-10 09:25:47 6 | * @LastEditors: PerkyRookie 7 | * @LastEditTime: 2019-07-19 14:09:30 8 | */ 9 | 10 | #include "pthread_pool.h" 11 | 12 | typedef struct task 13 | { 14 | /* 任务需要执行的函数 */ 15 | void *(*process)(int arg); 16 | /* 执行函数的参数 */ 17 | int arg; 18 | /* 下一个任务的地址 */ 19 | struct task *next; 20 | }Cthread_task; 21 | 22 | /*线程池任务结构*/ 23 | typedef struct 24 | { 25 | pthread_mutex_t queue_lock; 26 | pthread_cond_t queue_ready; 27 | 28 | /*链表结构,线程池中所有等待任务*/ 29 | Cthread_task *queue_head; 30 | /*是否销毁线程池*/ 31 | int shutdown; 32 | /*存放线程id的指针*/ 33 | pthread_t *threadid; 34 | /*线程池中线程数目*/ 35 | int max_thread_num; 36 | /*当前等待的任务数*/ 37 | int cur_task_size; 38 | 39 | } Cthread_pool; 40 | 41 | static Cthread_pool *pool = NULL; 42 | void *thread_routine(void *arg); 43 | 44 | /* 45 | * @description: 初始化线程池 46 | * @param {int} max_thread_num: 创建几个线程 47 | * @return: 无 48 | */ 49 | void pool_init(int max_thread_num) 50 | { 51 | pool = (Cthread_pool *)malloc(sizeof(Cthread_pool)); 52 | /*动态初始化互斥量*/ 53 | pthread_mutex_init(&(pool->queue_lock), NULL); 54 | /*初始化条件变量*/ 55 | pthread_cond_init(&(pool->queue_ready), NULL); 56 | //没有任务,头结点为空 57 | pool->queue_head = NULL; 58 | //定义最大线程个数 59 | pool->max_thread_num = max_thread_num; 60 | //现在任务为0 61 | pool->cur_task_size = 0; 62 | //线程池开始工作 63 | pool->shutdown = 0; 64 | //申请存放线程池id的数组 65 | pool->threadid = (pthread_t *)malloc(max_thread_num * sizeof(pthread_t)); 66 | 67 | for (int i = 0; i < max_thread_num; i++) 68 | { 69 | //创建线程,线程属性为空,参数也设置为空 70 | if(pthread_create(&(pool->threadid[i]), NULL, thread_routine, NULL) != 0) 71 | errExit("pool_init.pthread_create"); 72 | } 73 | } 74 | 75 | /* 76 | * @description: 线程运行函数,创建线程时使用,线程运行函数编程遵循如下步骤: 77 | * @description: 如果当前没有任务,线程被阻塞,等待任务加入唤醒线程,如果有任务加入,线程会被唤醒 78 | * @param {void *} arg: 传入的参数 79 | * @return: 无 80 | */ 81 | void *thread_routine(void *arg) 82 | { 83 | //获取线程id 84 | printf("starting thread 0x%lx\n", pthread_self()); 85 | while (1) 86 | { 87 | //加上互斥锁,动态 88 | pthread_mutex_lock(&(pool->queue_lock)); 89 | //如果没有任务,则阻塞,等待被唤醒 90 | while (pool->cur_task_size == 0 && !pool->shutdown) 91 | { 92 | printf("thread 0x%lx is waiting\n", pthread_self()); 93 | /*阻塞线程,直到收到条件变量queue_ready通知 */ 94 | pthread_cond_wait(&(pool->queue_ready), &(pool->queue_lock)); 95 | } 96 | /*线程池销毁时才会运行*/ 97 | if (pool->shutdown) 98 | { 99 | /*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/ 100 | pthread_mutex_unlock(&(pool->queue_lock)); 101 | printf("thread 0x%lx will exit\n", pthread_self()); 102 | pthread_exit(NULL); 103 | } 104 | /*有任务添加 */ 105 | printf("thread 0x%lx is starting to work\n", pthread_self()); 106 | 107 | /*待处理任务减1,并取出链表中的头元素*/ 108 | pool->cur_task_size--; 109 | Cthread_task *task = pool->queue_head; 110 | pool->queue_head = task->next; 111 | /*解锁*/ 112 | pthread_mutex_unlock(&(pool->queue_lock)); 113 | /*调用回调函数(通过pool传入的),执行任务*/ 114 | (*(task->process))(task->arg); 115 | free(task); 116 | task = NULL; 117 | } 118 | /*这一句应该是不可达的*/ 119 | pthread_exit(NULL); 120 | } 121 | 122 | /* 123 | * @description: 向线程池中加入任务 124 | * @param {void *} *(process): 线程执行的函数 125 | * @param {int} arg: 传入执行函数的参数 126 | * @return: 成功则返回0 127 | */ 128 | int pool_add_task (void *(*process) (int arg), int arg) 129 | { 130 | /*构造一个新任务*/ 131 | Cthread_task *task = (Cthread_task *)malloc(sizeof(Cthread_task)); 132 | task->process = process; 133 | task->arg = arg; 134 | task->next = NULL; 135 | 136 | pthread_mutex_lock(&(pool->queue_lock)); 137 | /*将任务加入到等待队列中*/ 138 | Cthread_task *member = pool->queue_head; 139 | if (member != NULL) 140 | { 141 | while (member->next != NULL) 142 | member = member->next; 143 | member->next = task; //直到结点为空时,加入任务 144 | } 145 | else 146 | { 147 | pool->queue_head = task; 148 | } 149 | //等待任务书+1 150 | pool->cur_task_size++; 151 | pthread_mutex_unlock(&(pool->queue_lock)); 152 | //唤醒一个线程 153 | pthread_cond_signal(&(pool->queue_ready)); 154 | 155 | return 0; 156 | } 157 | 158 | /* 159 | * @description: 销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直把任务运行完后再退出 160 | * @param {type} 无 161 | * @return: 成功返回0,错误返回-1 162 | */ 163 | int pool_destroy() 164 | { 165 | if (pool->shutdown) 166 | return -1; /*防止两次调用*/ 167 | pool->shutdown = 1; 168 | 169 | /*唤醒所有等待线程,线程池要销毁了*/ 170 | pthread_cond_broadcast(&(pool->queue_ready)); 171 | 172 | /*阻塞等待线程退出,否则就成僵尸了*/ 173 | int i; 174 | for (i = 0; i < pool->max_thread_num; i++) 175 | pthread_join(pool->threadid[i], NULL); 176 | free(pool->threadid); 177 | 178 | /*销毁等待队列*/ 179 | Cthread_task *head = NULL; 180 | while (pool->queue_head != NULL) 181 | { 182 | head = pool->queue_head; 183 | pool->queue_head = pool->queue_head->next; 184 | free(head); 185 | } 186 | /*条件变量和互斥量也别忘了销毁*/ 187 | pthread_mutex_destroy(&(pool->queue_lock)); 188 | pthread_cond_destroy(&(pool->queue_ready)); 189 | 190 | free(pool); 191 | /*销毁后指针置空是个好习惯*/ 192 | pool = NULL; 193 | return 0; 194 | } -------------------------------------------------------------------------------- /pthread_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef PTHREAD_POOL_H 2 | #define PTHREAD_POOL_H 3 | 4 | #include 5 | #include "tlpi_hdr.h" 6 | 7 | void *thread_routine(void *arg); 8 | void pool_init(int max_thread_num); 9 | void *thread_routine(void *arg); 10 | int pool_add_task (void *(*process) (int arg), int arg); 11 | int pool_destroy(); 12 | 13 | #endif // !PTHREAD_POOL_H -------------------------------------------------------------------------------- /server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coder-21Hertz/File-Transfer-System/0b5c984cc2ca3fa1519d8b3d8c6aa75384237fe6/server -------------------------------------------------------------------------------- /server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: 服务器程序 3 | * @Company: SZU 4 | * @Author: PerkyRookie 5 | * @Date: 2019-04-08 14:19:59 6 | * @LastEditors: PerkyRookie 7 | * @LastEditTime: 2019-07-19 14:22:32 8 | */ 9 | 10 | #include "login.h" 11 | #include "common.h" 12 | #include "pthread_pool.h" 13 | 14 | SSL_CTX *ctx; 15 | struct sockaddr_in clientaddr; 16 | 17 | void *myprocess(int args) 18 | { 19 | int connfd = args; 20 | char claddrStr[INET_ADDRSTRLEN]; 21 | /* 获取客户端ip地址 */ 22 | inet_ntop(AF_INET, &clientaddr.sin_addr, claddrStr, INET_ADDRSTRLEN); 23 | 24 | /* 基于 ctx 产生一个新的 SSL */ 25 | SSL *ssl = SSL_new(ctx); 26 | /* 将连接用户的 socket 加入到 SSL */ 27 | SSL_set_fd(ssl, connfd); 28 | /* 建立 SSL 连接 */ 29 | if (SSL_accept(ssl) == -1) 30 | errExit("myprocess.SSL_accept"); 31 | 32 | char buff[BUFFSIZE]; 33 | if(server_login(ssl) == 'q') 34 | { 35 | /* 关闭 SSL 连接 */ 36 | SSL_shutdown(ssl); 37 | /* 释放 SSL */ 38 | SSL_free(ssl); 39 | close(connfd); 40 | return NULL; 41 | } 42 | while(1) 43 | { 44 | memset(buff, 0, BUFFSIZE); 45 | 46 | /* 读取命令行输入信息 */ 47 | int len=SSL_read(ssl,buff,BUFFSIZE); 48 | if(len <= 0) 49 | errExit("myprocess.SSL_read.1"); 50 | 51 | if (*buff == 'q') 52 | break; 53 | /* 分割命令行输入信息 */ 54 | char filename[FILE_NAME_MAX_SIZE]; 55 | char operate = my_strtok(buff, filename); 56 | 57 | /* 发送文件到客户端 */ 58 | if(operate == 'r') 59 | { 60 | send_file(filename, ssl, claddrStr); 61 | } 62 | /* 从客户端读取文件 */ 63 | else if (operate == 's') 64 | { 65 | recv_file(filename, ssl, claddrStr); 66 | } 67 | } 68 | 69 | /* 关闭 SSL 连接 */ 70 | SSL_shutdown(ssl); 71 | /* 释放 SSL */ 72 | SSL_free(ssl); 73 | close(connfd); 74 | 75 | return NULL; 76 | } 77 | 78 | 79 | int main(int argc, char *argv[]) 80 | { 81 | // char sql[100]; 82 | 83 | if (argc != 3 || strcmp(argv[1], "--help") == 0) 84 | usageErr("%s cacert.pem privkey.pem\n", argv[0]); 85 | setbuf(stdout,NULL); //关闭缓冲 86 | 87 | //初始化线程池 88 | pool_init(5); 89 | 90 | /* SSL 库初始化 */ 91 | SSL_library_init(); 92 | /* 载入所有 SSL 算法 */ 93 | OpenSSL_add_all_algorithms(); 94 | /* 载入所有 SSL 错误消息 */ 95 | SSL_load_error_strings(); 96 | /* 以 SSL V2 和 V3 标准兼容方式产生一个 SSL_CTX ,即 SSL Content Text */ 97 | ctx = SSL_CTX_new(SSLv23_server_method()); 98 | /* 也可以用 SSLv2_server_method() 或 SSLv3_server_method() 单独表示 V2 或 V3标准 */ 99 | if (ctx == NULL) 100 | errExit("SSL_CTX_new"); 101 | /* 载入用户的数字证书, 此证书用来发送给客户端。 证书里包含有公钥 */ 102 | if (SSL_CTX_use_certificate_file(ctx, argv[1], SSL_FILETYPE_PEM) <= 0) 103 | errExit("SSL_CTX_use_certificate_file"); 104 | /* 载入用户私钥 */ 105 | if (SSL_CTX_use_PrivateKey_file(ctx, argv[2], SSL_FILETYPE_PEM) <= 0) 106 | errExit("SSL_CTX_use_PrivateKey_file"); 107 | /* 检查用户私钥是否正确 */ 108 | if (!SSL_CTX_check_private_key(ctx)) 109 | errExit("SSL_CTX_check_private_key"); 110 | 111 | /* 创建 socket */ 112 | int sockfd = socket_init(); 113 | if(sockfd == -1) 114 | errExit("socket_init"); 115 | printf("服务器启动成功!\n"); 116 | 117 | int connfd; 118 | 119 | while (1) 120 | { 121 | socklen_t length = sizeof(clientaddr); 122 | connfd = accept(sockfd, (struct sockaddr *)&clientaddr, &length); 123 | if (connfd == -1) 124 | errExit("accept"); 125 | if (getpeername(connfd, (struct sockaddr *)&clientaddr, &length) == -1) 126 | errExit("getpeername"); 127 | 128 | //给线程池添加任务 129 | pool_add_task(myprocess,connfd); 130 | } 131 | close(sockfd); 132 | SSL_CTX_free(ctx); 133 | return 0; 134 | } -------------------------------------------------------------------------------- /test/client/client: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coder-21Hertz/File-Transfer-System/0b5c984cc2ca3fa1519d8b3d8c6aa75384237fe6/test/client/client -------------------------------------------------------------------------------- /test/server/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDoDCCAoigAwIBAgIJAIKF8LblCfBqMA0GCSqGSIb3DQEBCwUAMGUxETAPBgNV 3 | BAcMCHNoZW56aGFuMQwwCgYDVQQKDANzenUxDDAKBgNVBAsMA3N6eTENMAsGA1UE 4 | AwwEc3N6dTElMCMGCSqGSIb3DQEJARYWeWtodWFuZy5pbml0QGdtYWlsLmNvbTAe 5 | Fw0xOTEwMjYwOTMwMzBaFw0yMjEwMjUwOTMwMzBaMGUxETAPBgNVBAcMCHNoZW56 6 | aGFuMQwwCgYDVQQKDANzenUxDDAKBgNVBAsMA3N6eTENMAsGA1UEAwwEc3N6dTEl 7 | MCMGCSqGSIb3DQEJARYWeWtodWFuZy5pbml0QGdtYWlsLmNvbTCCASIwDQYJKoZI 8 | hvcNAQEBBQADggEPADCCAQoCggEBALCVnv1AYBv3AN73q8Abb6751jNrpx8YKNbH 9 | yeBbgg/vc0XR6g90hOEsqHUDfG1ndTPMeApQr8jR+7pxRdrKNAtfwemDXlOtKdJ6 10 | DhLmKSUTfm3+OEWzGQLlHt+86/5UCZpDn335T+prYSNvvj49fkUySQF1RDNXaYVX 11 | OWuP7VwNt4hcRfBf2CyFvvf3YQOLI9mc/5+wX/+UqeowHF5TANqf7vnk+aKw9Ve3 12 | W/NwmyGfawu1scKfJlXMz4wfx4v1EIpSvmGS7cCWpZTvtTQrnRBKxzj57KmYI/f+ 13 | WY6/NZ5AfPXWVPGp6IOwOyi+wZzXyIfHfAdMrzHab0DuV3HTd9sCAwEAAaNTMFEw 14 | HQYDVR0OBBYEFDpG8umxbkbHAMFSyt6GQrGU9Y+pMB8GA1UdIwQYMBaAFDpG8umx 15 | bkbHAMFSyt6GQrGU9Y+pMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD 16 | ggEBAD/YKDhPTS3idhjbzQ/y+SCmTD/Bqwben42q7hYDKGVtl5LDkGeMspT1yIAe 17 | NGPa5VFlEj0KEvv4CzV0eToXv00KN7bdRD2jomvk9qx9f1iCBxHXthuJ1RFyfrjd 18 | HOU+oLmskwU3XRraWWnkWVo4ob+/76Z9Hio0p+iyduz9fOM9WkhdLKVWdXMFilM6 19 | 15lccYFBqpJcjmfOHSSkhwp6QA22dbaPJi9xQnsndBazf1u7Cjb3pJvusU0G75yc 20 | oAjmKcuZUUM5SP4BilQRHZ5dZejiF5KGcKpEprF81ecyKIjfZ4BJ8BAtzItS1xhC 21 | DQahfT4MukUCF+0h84EEUFgf9w4= 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /test/server/privkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAsJWe/UBgG/cA3verwBtvrvnWM2unHxgo1sfJ4FuCD+9zRdHq 3 | D3SE4SyodQN8bWd1M8x4ClCvyNH7unFF2so0C1/B6YNeU60p0noOEuYpJRN+bf44 4 | RbMZAuUe37zr/lQJmkOffflP6mthI2++Pj1+RTJJAXVEM1dphVc5a4/tXA23iFxF 5 | 8F/YLIW+9/dhA4sj2Zz/n7Bf/5Sp6jAcXlMA2p/u+eT5orD1V7db83CbIZ9rC7Wx 6 | wp8mVczPjB/Hi/UQilK+YZLtwJallO+1NCudEErHOPnsqZgj9/5Zjr81nkB89dZU 7 | 8anog7A7KL7BnNfIh8d8B0yvMdpvQO5XcdN32wIDAQABAoIBACGyS4aUNELh3Z6u 8 | sG2WTrebIU7qqhd66sAhm9peW/FihiK6remJGJpHhVXe9m2CIpFh4T5Gq2Ss/VZU 9 | qznXABtziHNhCCz7jFGHzQu6M/vJPoUKz/4s8QbmZf/X80izDKoNYXAOlTtshjrW 10 | 5fzGi0P1Fo1arUsG0TIKtQBblLXN7x1Aqwc/8flRB7VFowjhojUZvg31h2yu/51v 11 | Vx4wWBwWOeaQK2t5vwovrCNTVXMvF3p1EI1vCVRU9Ze6VoHhNict8f9a4RtNhdza 12 | w/0g31Ba8kOtjWeGaoFT45WWYkq7GDFDP4BMM0iGO0f2Nh3XeYFM9C0WaD5SW5af 13 | VpBEVvECgYEA2l8MBD8esGhpyjT8tsGRRbr4eevXC6OhXo0hN4Z4zQ36Xi29pH/e 14 | 2R2/trBKo20QCwTqjXwnGJYFmxxSzOgw5XCrx+o8guT5C8glzXT6pfouL8oxlnqg 15 | cyzcGSy8jnMGMBeU6l/7oSJgDt5AEJpK/BdZu/+q5Ed/oJX3YEfkKnUCgYEAzwM+ 16 | UakrWZsGhfiK5EJ0Ilorw/qaNk37JhlSwv7BtsI9s0DKj+SGz3b0My6baNqVIlay 17 | rf0Hx5ceU9LrMC9raLUZxr55VIAu1VHKgInUD/5xXcak8SlcxY403FXaCursSj2d 18 | 9367Q/edeO1oLDLJPWstz991I/Ie/ssqcbTTrw8CgYAvJD2v3QB/DMtpv66etYLr 19 | DsX9bBBsjtKTWtxE5F7HxbC7DBzYmlI9hNKSWdSDG1RPTKxmBttZz2k+Zwro+Wc0 20 | 4PH0oniVuWCaTob6p7pEgpHd90RlO+vPcWvCc4nxJpu7XhvjzPzBeG5MR2aFLwnp 21 | ChFxKGx4A4dl33ob6uky0QKBgCOcgTn3ChgODR861FnT/Lp0HJdIIwM4gv3Y2V3m 22 | CVjxLUAK403BmFdUNUoguGdfQsx3caFwp8fz/SmaXQ8a+TU4AkkoNyMMMgJMYbOf 23 | AjYGJWZmZ7V80hLCFeHZevrp0uAqnWQYDwrwK8zDumnwCqJgL69U14fpfN+D/7cz 24 | zj6NAoGAa1f4yOhuLi+/FVhut+fyT0b995vrGOxxR53M9DbPIMD+q7G5yhf7U5Ye 25 | hALolwTSh6iQjAzVDn7mRA7fRGhmPFvhi8J2yvx9Qry9W+JDLKJE+mY1vb9h0INO 26 | nNGdrMyT3rHOxBqljiIIn8A0rUqpLMMeoTBackHMmVRNuZpSm40= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/server/server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coder-21Hertz/File-Transfer-System/0b5c984cc2ca3fa1519d8b3d8c6aa75384237fe6/test/server/server --------------------------------------------------------------------------------