2023_open_source_contest_final_1st_issue3

This commit is contained in:
JasenChao 2023-09-18 10:55:05 +08:00
parent fb02dae0cd
commit 10e672371e
15 changed files with 192 additions and 0 deletions

View File

@ -0,0 +1,123 @@
# 基于初赛一级赛题3在云服务器上实现FTP Server功能
## 1. 简介
基于赛事提供的云服务器实现FTP协议的Server功能其功能支持至少10个Client端并发向Server端传输4KB大小的文件支持Server端并发地向至少10个Client端传输4KB大小的文件可通过在shell终端打印相关配置、相关截图说明来验证功能实现
## 2. 数据结构设计说明
### FTP client
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 个函数,其中 5 个为接口函数,分别为:
- `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
### FTP server
FTP server设计为
```c
typedef struct _ftp_server
{
int sock_data;
int sock_control;
int sock_listen;
int data_port;
char ip[64];
pthread_t tid;
void *arg;
} ftp_server;
```
一共实现了 12 个函数,分别为:
- `socket_create`:封装了创建 socket 的过程,参数 port 为指针,可以返回分配的端口号,便于使用动态端口作为数据端口
- `socket_accept`:封装了 accept 的过程
- `recv_data`:从 FTP client 接收数据
- `send_response`:向 FTP client 发送数据
- `retr`:实现 RETR 命令
- `list`:实现 LIST 命令
- `pasv`:实现 PASV 命令,分配动态端口
- `size`:实现 SIZE 命令
- `check_user`:检查用户名和密码是否正确
- `login`:实现 client 登陆的过程
- `recv_cmd`:解析 client 发送的命令,调用对应的处理函数
- `process`server 的进程函数
## 3. 测试程序说明
测试程序 `TestFtpClientFinal` 已经注册为 shell 命令,可以调用执行。编译时需要打开 lwip 和 USB。
1. 首先编译运行 FTP server通过命令 `gcc -pthread ftpserver.c -o ftp_server` 编译,再使用 `./ftp_server 9991` 命令运行,端口号可以任意选择未被占用的端口,此处选择大赛规定的端口之一,同时保持 client 配置的端口与 server 一致。
2. 在 server 目录下生成了 10 个 4KB 大小的测试文件
![fig0](fig0.png)
3. 将开发板与路由器连接,根据实际情况修改代码中的网卡配置部分,使得开发板可以上网。
4. `TestFtpClientFinal` 命令需要一个参数,参数可选 `upload``download`,分别会启动 10 个线程,进行文件名拼接,然后登陆到 FTP server进行对应文件的上传/下载。注意需要将测试程序中 `ftp_login` 的 server ip、用户名以及密码替换为实际情况的内容。
5. 关闭 FTP client。
## 4. 运行结果(需结合运行测试截图按步骤说明)
1. 在 server 端编译代码并运行.
![fig1](fig1.png)
2. 在 client 代码工作区终端中进入 `menuconfig` 配置页面,打开 `Using LwIP by ethernet device`、`Using USB device` 以及 `test app` 中的 `Config test ftp client`
3. 执行编译命令:`make BOARD=edu-arm32`,正常情况下应当编译无误,将编译好的 bin 文件烧录进开发板。
4. 在开发板的 USB 接口上接入 U 盘,启动开发板,通过串口终端观察 USB 挂载成功,用 `ls` 命令可以看到根目录为空。
![fig2](fig2.png)
5. 执行 `TestFtpClientFinal download` 命令10 个线程并发创建 10 个 client 下载对应的 10 个文件,可以看到下载过程是并发进行的。
![fig3](fig3.png)
![fig4](fig4.png)
![fig5](fig5.png)
![fig6](fig6.png)
6. 执行 `ls` 命令,看到文件全部下载成功,大小与 server 一致。
![fig7](fig7.png)
7. 在 server 端执行 `rm file_*` 命令,删除服务器端的测试文件。
8. 执行 `TestFtpClientFinal upload` 命令10 个线程并发创建 10 个 client 上传刚刚下载的 10 个文件,可以看到上传过程是并发进行的。
![fig8](fig8.png)
![fig9](fig9.png)
![fig10](fig10.png)
![fig11](fig11.png)
9. 在 server 端执行 `ll --block-size=1` 命令,可以看到文件全部重新上传,大小与之前一致。
![fig12](fig12.png)
10. 测试结束。

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -0,0 +1,69 @@
#include "../test_ftpclient/ftp_client/ftp_client.h"
#include "lwip/sys.h"
static ftp_client *f[10];
static void *downloadfunc(void *param) {
int i = *(int *)param;
/* Use the appropriate username and password as login parameters */
f[i] = ftp_login("8.140.53.225", "user", "pass");
if (f[i] != NULL) {
/* Choosing the right path */
// ftp_changedir(f[i], "test");
char filename[64];
sprintf(filename, "file_%d.txt", i);
ftp_downloadfile(f[i], filename);
/* Close FTP client */
ftp_quit(f[i]);
}
}
static void *uploadfunc(void *param) {
int i = *(int *)param;
/* Use the appropriate username and password as login parameters */
f[i] = ftp_login("8.140.53.225", "user", "pass");
if (f[i] != NULL) {
char filename[64];
sprintf(filename, "file_%d.txt", i);
ftp_uploadfile(f[i], filename);
/* Close FTP client */
ftp_quit(f[i]);
}
}
void TestFtpClientFinal(int argc, char *argv[]) {
if (argc != 2) {
printf("Need a param.\n");
return;
}
pthread_attr_t attr;
attr.schedparam.sched_priority = 22;
attr.stacksize = 4096;
memset(f, 0, sizeof(f));
/* 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);
if (strcmp(argv[1], "upload") == 0) {
/* Upload 10 files of about 4kb in size */
for (int i = 1; i <= 10; ++i) {
pthread_t up;
PrivTaskCreate(&up, &attr, &uploadfunc, &i);
}
} else if (strcmp(argv[1], "download") == 0) {
/* Download 10 files of about 4kb in size */
for (int i = 1; i <= 10; ++i) {
pthread_t down;
PrivTaskCreate(&down, &attr, &downloadfunc, &i);
}
}
}
PRIV_SHELL_CMD_FUNCTION(TestFtpClientFinal, 2023 open source contest final 1 st issue3, PRIV_SHELL_CMD_MAIN_ATTR);