diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 6b0c1ea96..7cf98070b 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -134,7 +134,7 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) endif ifeq ($(CONFIG_USER_TEST_FTPCLIENT),y) - SRC_FILES += + SRC_FILES += test_ftpclient/test_ftpclient.c test_ftpclient/ftp_client/ftp_client.c endif ifeq ($(CONFIG_USER_TEST_LORA_P2P),y) diff --git a/APP_Framework/Applications/app_test/test_ftpclient/README.md b/APP_Framework/Applications/app_test/test_ftpclient/README.md new file mode 100644 index 000000000..6701feb78 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient/README.md @@ -0,0 +1,80 @@ +# # 基于k210-emulator实现基数树并测试验证 + +## 1. 简介 + +基于矽璓已实现的Lwip,在ARM上实现FTP协议的Client功能,并编写测试程序在shell终端打印结果。 + +## 2. 数据结构设计说明 + +FTP client设计为: + +```c +typedef struct ftp_client_struct +{ + char username[64]; + char password[64]; + char server_ip[64]; + char data_port[6]; + char control_port[6]; + int control_socket; + int data_socket; +}ftp_client; +``` + +一共实现了 8 个函数,其中 4 个为接口函数,分别为: + +- `ftp_cmd`:向 FTP server 发送指令 +- `ftp_downloaddata`:从 FTP server 接收数据 +- `ftp_uploaddata`:向 FTP server 上传数据 +- `ftp_login`:登陆 FTP server +- `ftp_downloadfile`:从 FTP server 下载文件 +- `ftp_uploadfile`:向 FTP server 上传文件 +- `ftp_changedir`:切换目录 +- `ftp_quit`:关闭 FTP client + +## 3. 测试程序说明 + +测试程序 `TestFtpClient` 已经注册为 shell 命令,可以调用执行。编译时需要打开 lwip。 + +1. 首先确保个人电脑已安装 FTP server,通过 `bash` 程序在 server 上的 `test` 目录下生成了 10 个 4KB 大小的测试文件,其中前 3 个文件由随机生成的 ASCII 乱码填充,后7个文件由数字填充。 + + ![fig0](fig0.png) + +2. 将开发板与个人电脑通过以太网连接,设置个人电脑的以太网网卡 IP 为 `192.168.130.78`,子网掩码为 `255.255.254.0`,网关为 `192.168.130.1`。测试程序开始时会将开发板网卡 IP 初始化为 `192.168.130.77`,开发板和个人电脑处于同一网段下,可以相互通信。 + +3. 登陆到 FTP server,如果 server 匿名访问受限,则此时需要将测试程序中 `ftp_login("192.168.130.78", "anonymous", "anonymous")` 的用户名和密码替换为对应的内容。 + +4. 切换目录到 `test`,需要根据实际情况修改。 + +5. 根据 10 个文件的文件名进行文件下载,文件内容通过终端打印输出。 + +6. 关闭 FTP client。 + +## 4. 运行结果(需结合运行测试截图按步骤说明) + +首先确保个人电脑已安装 FTP server,并已经和开发板正确配置和连接。 + +1. 在工作区终端中进入 `menuconfig` 配置页面,打开 `Using LwIP by ethernet device` 以及 `test app` 中的 `Config test ftp client`。 + +2. 执行编译命令:`make BOARD=edu-arm32`,正常情况下应当编译无误,将编译好的 bin 文件烧录进开发板。 + +3. 启动开发板,通过串口终端观察输出,执行 `TestFtpClient` 命令。 + +4. 输出显示网卡初始化并连接到 server 成功,用户名密码校验通过,并切换至目标目录。 + + ![fig1](fig1.png) + +5. 开始下载文件,观察输出的内容,通过比对,与 server 上的文件内容一致,接收到的数据长度也和 server 返回的数据长度相等。 + + ![fig2](fig2.png) + ![fig3](fig3.png) + ![fig4](fig4.png) + ![fig5](fig5.png) + ![fig6](fig6.png) + ![fig7](fig7.png) + ![fig8](fig8.png) + ![fig9](fig9.png) + ![fig10](fig10.png) + ![fig11](fig11.png) + +6. 关闭 FTP client,测试结束。 \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig0.png b/APP_Framework/Applications/app_test/test_ftpclient/fig0.png new file mode 100644 index 000000000..cf2170146 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig0.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig1.png b/APP_Framework/Applications/app_test/test_ftpclient/fig1.png new file mode 100644 index 000000000..1bf3b8b9f Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig1.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig10.png b/APP_Framework/Applications/app_test/test_ftpclient/fig10.png new file mode 100644 index 000000000..48532d56c Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig10.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig11.png b/APP_Framework/Applications/app_test/test_ftpclient/fig11.png new file mode 100644 index 000000000..10cbafd89 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig11.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig2.png b/APP_Framework/Applications/app_test/test_ftpclient/fig2.png new file mode 100644 index 000000000..cea8bc49e Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig2.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig3.png b/APP_Framework/Applications/app_test/test_ftpclient/fig3.png new file mode 100644 index 000000000..8ecb44832 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig3.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig4.png b/APP_Framework/Applications/app_test/test_ftpclient/fig4.png new file mode 100644 index 000000000..44abb51ca Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig4.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig5.png b/APP_Framework/Applications/app_test/test_ftpclient/fig5.png new file mode 100644 index 000000000..2029df413 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig5.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig6.png b/APP_Framework/Applications/app_test/test_ftpclient/fig6.png new file mode 100644 index 000000000..80a0c35f1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig6.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig7.png b/APP_Framework/Applications/app_test/test_ftpclient/fig7.png new file mode 100644 index 000000000..e5ac129ed Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig7.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig8.png b/APP_Framework/Applications/app_test/test_ftpclient/fig8.png new file mode 100644 index 000000000..9fc6985b7 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig8.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/fig9.png b/APP_Framework/Applications/app_test/test_ftpclient/fig9.png new file mode 100644 index 000000000..df9fe33ff Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/fig9.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/ftp_client.c b/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/ftp_client.c new file mode 100644 index 000000000..130f67207 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/ftp_client.c @@ -0,0 +1,673 @@ +/** + * @file: ftp_client.c + * @brief: Implement FTP client + * @version: 1.0 + * @date: 2023/8/30 + */ + +#include +#include "ftp_client.h" +#include "lwip/sockets.h" +#include "lwip/netdb.h" + +/** + * @description: Send a cmd to FTP server + * @param socket - Control socket + * @param wait_time - Select timeout period + * @param cmd - Cmd code + * @param param - Store input parameter if needed + * @param other - Store output parameter if needed + * @return 0: success; others: failure + */ +static int ftp_cmd(int socket, int wait_time, int cmd, char *param, int *other) +{ + fd_set readset; + struct timeval timeout; + char buff[128] = {0}; + int len = 0; + int code= 0; + char cmdbuf[128] = {0}; + + int a,b,c,d,e,f = 0; + + timeout.tv_sec = wait_time/1000; + timeout.tv_usec = 0; + + FD_ZERO(&readset); + FD_SET(socket, &readset); + + /* Determine if a command needs to be sent and what to send based on cmd */ + switch (cmd) + { + case CWD: + sprintf(cmdbuf, "CWD %s\r\n", param); + len = send(socket, cmdbuf, strlen(cmdbuf), 0); + if (len < 0) { + LOG_I("Send cmd failed.\n"); + return -1; + } + break; + + case PASV: + sprintf(cmdbuf, "PASV\r\n"); + len = send(socket, cmdbuf, strlen(cmdbuf), 0); + if (len < 0) { + LOG_I("Send cmd failed.\n"); + return -1; + } + break; + + case SIZE: + sprintf(cmdbuf, "SIZE %s\r\n", param); + len = send(socket, cmdbuf, strlen(cmdbuf), 0); + if (len < 0) { + LOG_I("Send cmd failed.\n"); + return -1; + } + break; + + case RETR: + sprintf(cmdbuf, "RETR %s\r\n", param); + len = send(socket, cmdbuf, strlen(cmdbuf), 0); + if (len < 0) { + LOG_I("Send cmd failed.\n"); + return -1; + } + break; + + case USER: + sprintf(cmdbuf, "USER %s\r\n", param); + len = send(socket, cmdbuf, strlen(cmdbuf), 0); + if (len < 0) { + LOG_I("Send cmd failed.\n"); + return -1; + } + break; + + case PASS: + sprintf(cmdbuf, "PASS %s\r\n", param); + len = send(socket, cmdbuf, strlen(cmdbuf), 0); + if (len < 0) { + LOG_I("Send cmd failed.\n"); + return -1; + } + break; + + case TYPE: + len = send(socket, "TYPE I\r\n", strlen("TYPE I\r\n"), 0); + if (len < 0) { + LOG_I("Send cmd failed.\n"); + return -1; + } + break; + + case QUIT: + len = send(socket, "QUIT\r\n", strlen("QUIT\r\n"), 0); + if (len < 0) { + LOG_I("Send cmd failed.\n"); + return -1; + } + break; + + case STOR: + sprintf(cmdbuf, "STOR %s\r\n", param); + len = send(socket, cmdbuf, strlen(cmdbuf), 0); + if (len < 0) { + LOG_I("Send cmd failed.\n"); + return -1; + } + break; + + default: + break; + } + + /* Waiting for server response */ + if (select(socket + 1, &readset, NULL, NULL, &timeout) <= 0) { + LOG_I("select the socket timeout!\n"); + return -1; + } + + /* Wait and receive the packet back from the server. */ + int i = 0; + len = 0; + while (1) { + len = recv(socket, (char*)(buff + i), 1, 0); + if (len < 0) { + LOG_I("reading from socket error!\n"); + break; + } + if (*(buff + i) == '\n') break; // Stop receiving when a line break is encountered + ++i; + } + buff[i + 1] = '\0'; + LOG_I("Recv:%s\n", buff); + + /* Identify if the status code is correct according to cmd */ + switch (cmd) + { + case NOCMD: + if (1 == sscanf(buff, "%d", &code)) { + if (code != 220) { + LOG_I("Server no ack.\n"); + return -1; + } + LOG_I("Server ack.\n"); + return 0; + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case FINISH: + if (1 == sscanf(buff, "%d", &code)) { + if (code != 226) { + LOG_I("Server no ack.\n"); + return -1; + } + LOG_I("Server ack.\n"); + return 0; + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case CWD: + if (1 == sscanf(buff, "%d", &code)) { + if (code != 250) { + LOG_I("Switch dir error.\n"); + return -1; + } + LOG_I("Switch dir ok.\n"); + return 0; + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case PASV: + if (7 == sscanf(buff, "%d%*[^(](%d,%d,%d,%d,%d,%d", &code,&a,&b,&c,&d,&e,&f)) { + if (code != 227) { + LOG_I("Enter pasv mode error.\n"); + return -1; + } + *other = e * 256 + f; + LOG_I("Enter pasv mode ok.\n"); + return 0; + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case SIZE: + if (2 == sscanf(buff, "%d %d", &code, other)) { + if (code != 213) { + LOG_I("Get size error.\n"); + return -1; + } + LOG_I("Get size ok.\n"); + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case RETR: + if (2 == sscanf(buff, "%d%*[^(](%d", &code, other)) { + if (code != 150) { + LOG_I("Download file error.\n"); + return -1; + } + LOG_I("Download file ok.\n"); + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case USER: + if (1 == sscanf(buff, "%d", &code)) { + if (code != 331) { + LOG_I("Username error.\n"); + return -1; + } + LOG_I("Username ok.\n"); + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case PASS: + if (1 == sscanf(buff, "%d", &code)) { + if (code != 230) { + LOG_I("Password error.\n"); + return -1; + } + LOG_I("Password ok.\n"); + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case TYPE: + if (1 == sscanf(buff, "%d", &code)) { + if (code != 200) { + LOG_I("Bin mode error.\n"); + return -1; + } + LOG_I("Bin mode ok.\n"); + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case QUIT: + if (1 == sscanf(buff, "%d", &code)) { + if (code != 221) { + LOG_I("Quit error.\n"); + return -1; + } + LOG_I("Quit ok.\n"); + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + case STOR: + if (1 == sscanf(buff, "%d", &code)) { + if (code != 150) { + LOG_I("Upload error.\n"); + return -1; + } + LOG_I("Upload ok.\n"); + } else { + LOG_I("Server no ack.\n"); + return -1; + } + break; + + default: + break; + } +} + +/** + * @description: Recv data from FTP server + * @param socket - Data socket + * @param wait_time - Select timeout period + * @param buf - Buffer for storing data + * @param bufsize - Buffer size + * @param getlen - Actual length of data received + * @return 0: success; others: failure + */ +static int ftp_downloaddata(int socket, int wait_time, char *buf, int bufsize, int *getlen) +{ + fd_set readset; + struct timeval timeout; + int len = 0; + + timeout.tv_sec = wait_time/1000; + timeout.tv_usec = 0; + + FD_ZERO(&readset); + FD_SET(socket, &readset); + + /* Waiting for server response */ + if (select(socket + 1, &readset, NULL, NULL, &timeout) <= 0) { + LOG_I("select the socket timeout!"); + return -1; + } + + /* Wait and receive the packet back from the server. */ + len = recv(socket, (char*)buf, bufsize, 0); + if (len < 0) { + LOG_I("Reading data socket error!"); + return -1; + } else { + *getlen = len; + return 0; + } +} + +/** + * @description: Send data to FTP server + * @param socket - Data socket + * @param buf - Buffer for storing data + * @param bufsize - Buffer size + * @param getlen - Actual length of data sended + * @return 0: success; others: failure + */ +static int ftp_uploaddata(int socket, char *buf, int bufsize, int *getlen) +{ + int len = 0; + + len = send(socket, (char*)buf, bufsize, 0); + if (len < 0) { + LOG_I("Writing data socket error!"); + return -1; + } else { + *getlen = len; + return 0; + } +} + +/** + * @description: Login to FTP server + * @param addr - Server IP + * @param username - FTP username + * @param password - FTP password + * @return ftp_client pointer + */ +ftp_client *ftp_login(char *addr, char *username, char *password) +{ + int ret; + int fd = -1; + ftp_client *ftp = NULL; + int ack = 0; + char cmd[64] = {0}; + struct addrinfo hints, *result = NULL, *cur = NULL; + + /* Initialize FTP client */ + ftp = malloc(sizeof(ftp_client)); + if(!ftp) { + LOG_I("Malloc failed.\n"); + return NULL; + } + memset(ftp, 0, sizeof(ftp_client)); + ftp->control_socket = -1; + ftp->data_socket = -1; + + /* Save username password and ip */ + strncpy(ftp->username, username, strlen(username)); + strncpy(ftp->password, password, strlen(password)); + strncpy(ftp->server_ip, addr, strlen(addr)); + strncpy(ftp->control_port, CONTROL_PORT, strlen(CONTROL_PORT)); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + /* Connect to control port */ + if (getaddrinfo(ftp->server_ip, ftp->control_port, &hints, &result) != 0) goto __exit; + + for (cur = result; cur != NULL; cur = cur->ai_next) { + fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol); + if (fd < 0) continue; + if (connect(fd, cur->ai_addr, cur->ai_addrlen) == 0) { + ftp->control_socket = fd; + break; + } + } + + freeaddrinfo(result); + + /* Verify user name and password */ + if (ftp->control_socket >= 0) { + if (ftp_cmd(ftp->control_socket, 10000, NOCMD, NULL, NULL) != 0) { + goto __exit; + } + + if (ftp_cmd(ftp->control_socket, 10000, USER, ftp->username, NULL) != 0) { + goto __exit; + } + + if (ftp_cmd(ftp->control_socket, 10000, PASS, ftp->password, NULL) != 0) { + goto __exit; + } + + // if (ftp_cmd(ftp->control_socket, 10000, TYPE, NULL, NULL) != 0) { + // goto __exit; + // } + + return ftp; + } + +__exit: + if (ftp->control_socket >= 0) { + closesocket(ftp->control_socket); + ftp->control_socket = -1; + } + free(ftp); + LOG_I("Something wrong.\n"); + + return NULL; +} + +/** + * @description: Download file from FTP server + * @param ftp - ftp_client pointer + * @param file_name - Name of the file to be downloaded + * @return 0: success; others: failure + */ +int ftp_downloadfile(ftp_client *ftp, char *file_name) +{ + int ret = 0, fd = 0; + struct addrinfo hints, *result = NULL, *cur = NULL; + int getlen = 0; + int file_pos = 0; + int port = 0; + int file_size = 0; + + char filebuf[128] = {0}; + + /* Enter the pasv mode */ + if (ftp_cmd(ftp->control_socket, 10000, PASV, NULL, &port) != 0) { + ret = -1; + goto __exit; + } + + /* Creat the data socket link */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + sprintf(ftp->data_port, "%d", port); + + /* Do name resolution with both IPv6 and IPv4 */ + if (getaddrinfo(ftp->server_ip, ftp->data_port, &hints, &result) != 0) { + goto __exit; + } + + for (cur = result; cur != NULL; cur = cur->ai_next) { + fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol); + if (fd < 0) { + continue; + } + + if (connect(fd, cur->ai_addr, cur->ai_addrlen) == 0) { + ftp->data_socket = fd; + break; + } + closesocket(fd); + + } + freeaddrinfo(result); + + if(ftp->data_socket < 0) + { + ret = -1; + goto __exit; + } + + /* Send download the file command */ + if (ftp_cmd(ftp->control_socket, 10000, RETR, file_name, &file_size) != 0) { + ret = -1; + goto __exit; + } + + /* Waite the download file finish */ + if (ftp_cmd(ftp->control_socket, 10000, FINISH, NULL, NULL) != 0) { + ret = -1; + } else { + ret = 0; + } + + /* Read the file */ + while(file_size > file_pos) { + if (ftp_downloaddata(ftp->data_socket, 10000, filebuf, 128, &getlen) == 0) { + if (getlen) { + printf("%s\n", filebuf); // Temporarily process the document as a printout + } else { + ret = -1; + goto __exit; + } + } else { + ret = -1; + goto __exit; + } + file_pos = file_pos + getlen; + } + printf("Total length: %d\n", file_pos); // Compare the size of the file previously returned by the server + +__exit: + if(ftp->data_socket >= 0) + { + closesocket(ftp->data_socket); + ftp->data_socket = -1; + } + + return ret; +} + +/** + * @description: Upload file to FTP server + * @param ftp - ftp_client pointer + * @param file_name - Name of the file to be uploaded + * @return 0: success; others: failure + */ +int ftp_uploadfile(ftp_client *ftp, char *file_name) +{ + int ret = 0, fd = 0; + struct addrinfo hints, *result = NULL, *cur = NULL; + int getlen = 0; + int file_pos = 0; + int port = 0; + int file_size = 0; + + char filebuf[128] = {0}; + + /* Enter the pasv mode */ + if (ftp_cmd(ftp->control_socket, 10000, PASV, NULL, &port) != 0) { + ret = -1; + goto __exit; + } + + /* Creat the data socket link */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + sprintf(ftp->data_port, "%d", port); + + /* Do name resolution with both IPv6 and IPv4 */ + if (getaddrinfo(ftp->server_ip, ftp->data_port, &hints, &result) != 0) { + goto __exit; + } + + for (cur = result; cur != NULL; cur = cur->ai_next) { + fd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol); + if (fd < 0) { + continue; + } + + if (connect(fd, cur->ai_addr, cur->ai_addrlen) == 0) { + ftp->data_socket = fd; + break; + } + closesocket(fd); + + } + freeaddrinfo(result); + + if(ftp->data_socket < 0) + { + ret = -1; + goto __exit; + } + + /* Send upload the file command*/ + if (ftp_cmd(ftp->control_socket, 10000, STOR, file_name, NULL) != 0) { + ret = -1; + goto __exit; + } + + /* TODO: update file_size */ + + while(file_size > file_pos) { + /* TODO: read file and update filebuf */ + if (ftp_uploaddata(ftp->data_socket, filebuf, 128, &getlen) == 0) { + if (!getlen) { + ret = -1; + goto __exit; + } + } else { + ret = -1; + goto __exit; + } + file_pos = file_pos + getlen; + } + printf("Total length: %d\n", file_pos); + + /* Uploading a file requires the client to actively close the socket. */ + if(ftp->data_socket >= 0) + { + closesocket(ftp->data_socket); + ftp->data_socket = -1; + } + + /* Waite the upload file finish */ + if (ftp_cmd(ftp->control_socket, 10000, FINISH, NULL, NULL) != 0) { + ret = -1; + } else { + ret = 0; + } + +__exit: + if(ftp->data_socket >= 0) + { + closesocket(ftp->data_socket); + ftp->data_socket = -1; + } + + return ret; +} + +/** + * @description: Switch the directory + * @param ftp - ftp_client pointer + * @param dir - Name of the directory + * @return 0: success; others: failure + */ +int ftp_changedir(ftp_client *ftp, char *dir) +{ + if (ftp_cmd(ftp->control_socket, 10000, CWD, dir, NULL) == 0) { + return 0; + } + return -1; +} + +/** + * @description: Quit the FTP client + * @param ftp - ftp_client pointer + * @return 0: success; others: failure + */ +int ftp_quit(ftp_client *ftp) +{ + ftp_cmd(ftp->control_socket, 10000, QUIT, NULL, NULL); + closesocket(ftp->control_socket); + ftp->control_socket = -1; + + free(ftp); + return 0; +} \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/ftp_client.h b/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/ftp_client.h new file mode 100644 index 000000000..e1dae4247 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/ftp_client.h @@ -0,0 +1,40 @@ +/** + * @file: ftp_client.h + * @brief: Implement FTP client + * @version: 1.0 + * @date: 2023/8/30 + */ + +#include + +#define CONTROL_PORT "21" + +#define NOCMD 0 +#define PASV 1 +#define CWD 2 +#define SIZE 3 +#define RETR 4 +#define FINISH 5 +#define USER 6 +#define PASS 7 +#define TYPE 8 +#define QUIT 9 +#define STOR 10 + +#define LOG_I printf + +typedef struct ftp_client_struct +{ + char username[64]; + char password[64]; + char server_ip[64]; + char data_port[6]; + char control_port[6]; + int control_socket; + int data_socket; +}ftp_client; + +ftp_client *ftp_login(char *addr, char *username, char *password); +int ftp_downloadfile(ftp_client *ftp, char *file_name); +int ftp_changedir(ftp_client *ftp, char *dir); +int ftp_quit(ftp_client *ftp); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient/test_ftpclient.c b/APP_Framework/Applications/app_test/test_ftpclient/test_ftpclient.c new file mode 100644 index 000000000..0a80c2378 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient/test_ftpclient.c @@ -0,0 +1,27 @@ +#include "ftp_client/ftp_client.h" +#include "lwip/sys.h" + +void TestFtpClient() { + /* Initialize the network IP */ + char self_ipaddr[] = {192, 168, 130, 77}; + char self_netmask[] = {255, 255, 254, 0}; + char self_gwaddr[] = {192, 168, 130, 1}; + lwip_config_tcp(0, self_ipaddr, self_netmask, self_gwaddr); + + /* Use the appropriate username and password as login parameters */ + ftp_client *f = ftp_login("192.168.130.78", "why", "7355"); + + /* Choosing the right path */ + ftp_changedir(f, "test"); + + /* Download 10 files of about 4kb in size */ + for (int i = 1; i <= 10; ++i) { + char filename[] = {0}; + sprintf(filename, "file_%d.txt", i); + ftp_downloadfile(f, filename); + } + + /* Close FTP client */ + ftp_quit(f); +} +PRIV_SHELL_CMD_FUNCTION(TestFtpClient, Download 10 files from server, PRIV_SHELL_CMD_MAIN_ATTR); \ No newline at end of file diff --git a/APP_Framework/lib/lorawan/lora_radio_driver b/APP_Framework/lib/lorawan/lora_radio_driver index a94c007cb..d21965b1c 160000 --- a/APP_Framework/lib/lorawan/lora_radio_driver +++ b/APP_Framework/lib/lorawan/lora_radio_driver @@ -1 +1 @@ -Subproject commit a94c007cb4ee726cc29b10626f8bbfc19c989b89 +Subproject commit d21965b1cbcfa99b2d36acd029a37f3f2eba612e diff --git a/APP_Framework/lib/lorawan/lorawan_devicenode b/APP_Framework/lib/lorawan/lorawan_devicenode index 254754bc7..2896d7234 160000 --- a/APP_Framework/lib/lorawan/lorawan_devicenode +++ b/APP_Framework/lib/lorawan/lorawan_devicenode @@ -1 +1 @@ -Subproject commit 254754bc7d06011cbec4655cd229c8ccfb95240b +Subproject commit 2896d7234688de77992e7e1872a7e67a9456b420