2023_open_source_contest_preliminary_1st_issue3
|
@ -134,7 +134,7 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG_USER_TEST_FTPCLIENT),y)
|
ifeq ($(CONFIG_USER_TEST_FTPCLIENT),y)
|
||||||
SRC_FILES +=
|
SRC_FILES += test_ftpclient/test_ftpclient.c test_ftpclient/ftp_client/ftp_client.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG_USER_TEST_LORA_P2P),y)
|
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
|