2023_open_source_contest_preliminary_1st_issue3
|
@ -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)
|
||||
|
|
|
@ -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个文件由数字填充。
|
||||
|
||||

|
||||
|
||||
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 成功,用户名密码校验通过,并切换至目标目录。
|
||||
|
||||

|
||||
|
||||
5. 开始下载文件,观察输出的内容,通过比对,与 server 上的文件内容一致,接收到的数据长度也和 server 返回的数据长度相等。
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
6. 关闭 FTP client,测试结束。
|
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 183 KiB |
After Width: | Height: | Size: 360 KiB |
After Width: | Height: | Size: 359 KiB |
After Width: | Height: | Size: 358 KiB |
After Width: | Height: | Size: 177 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 177 KiB |
After Width: | Height: | Size: 177 KiB |
|
@ -0,0 +1,673 @@
|
|||
/**
|
||||
* @file: ftp_client.c
|
||||
* @brief: Implement FTP client
|
||||
* @version: 1.0
|
||||
* @date: 2023/8/30
|
||||
*/
|
||||
|
||||
#include <transform.h>
|
||||
#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;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* @file: ftp_client.h
|
||||
* @brief: Implement FTP client
|
||||
* @version: 1.0
|
||||
* @date: 2023/8/30
|
||||
*/
|
||||
|
||||
#include <transform.h>
|
||||
|
||||
#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);
|
|
@ -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);
|
|
@ -1 +1 @@
|
|||
Subproject commit a94c007cb4ee726cc29b10626f8bbfc19c989b89
|
||||
Subproject commit d21965b1cbcfa99b2d36acd029a37f3f2eba612e
|
|
@ -1 +1 @@
|
|||
Subproject commit 254754bc7d06011cbec4655cd229c8ccfb95240b
|
||||
Subproject commit 2896d7234688de77992e7e1872a7e67a9456b420
|