本来我是觉得这个项目很拿不出手的,毕竟最后也没有经过压力测试等。
甚至在投简历的时候都不敢提交上去。
但是昨天和辅导员交流之后,有什么不好意思的,就算我现在在做一个项目,也是难以达到人家的预期的。
但是以一个大二,甚至大三的非软工专业的学生独立设计并写出这个项目,有什么好丢人的?
我们先来看一下功能需求吧:
模块名 | 功能类别 | 子功能 |
---|---|---|
本地云客户端 | 系统启动 | 本地云启动,初始化界面,软件版本以及其他初始化数据同步 |
| 用户注册 | 用户输入用户名、密码、密保手机号,注册一个新账号 |
| 用户登录 | 用户输入用户名、密码,登录到服务器获取用户储存的文件列表 |
| 修改密码 | 用户输入用户名、旧密码、新密码,完成修改密码 |
| 找回密码 | 用户输入用户名、密保手机,发送到服务器获取密码 |
| 文件列表 | 获取当前文件夹下应该出现的文件列表 |
| 文件上传 | 上传文件到指定文件夹 |
| 文件下载 | 从指定文件夹下载文件 |
| 本地下载文件管理 | 下载之后的文件应由本地文件夹管理 |
| 通信纪录 | 文件上传纪录、下载纪录获取 |
| 文件共享 | 生成链接与提取码,可将文件分享给其他用户 |
| 系统设置 | 配置服务的ip地址与端口等设置 |
– | – | – |
本地云服务器 | 用户接入 | 接收客户端的连接,管理客户端信息 |
| 各业务处理 | 处理客户端的各类型业务 |
| 心跳处理 | 各组自定义 |
| 文件管理 | 对客户传输的文件进行管理 |
| 数据库设计与应用 | 各组自定义,服务于文件管理 |
| 并发负载 | 初始化30的处理线程 |
| 日志 | 每天生成一个运行日志,包括业务处理、运行情况、故障记录等 |
– | – | – |
压力测试程序 | 单客户单业务测试 | 主要测试服务器单笔业务的处理能力和数据准确性 |
| 多客户多业务 | 不同种类业务,测试服务器吞吐量 |
| 测试报告 | 给出关键参数和测试结果,如:业务成功数、失败数、业务类型等 |
| 压力参数设置 | 每次测试时,用户可以自定义测试时长、业务类型、模拟客户端数等,并能保存到参数文件中 |
| 测试日志 | 记录每次测试结果,每天生成一个 |
以下为非功能性需求:
功能类别 | 功能说明 |
---|---|
万级并发 | 要求一台服务器能够承受10000台以上的连接数接入 |
大并发业务 | 要求一台服务器能承受200-300并发业务处理 |
分布架构 | 采用TCP流协议进行进程间通信 |
系统拓展性 | 能够在尽量不修改源代码的前提下,拓展业务 |
系统安全 | 承受一些非法操作的轰炸 |
其实主要做的也就是这些非功能性需求,上面那些功能需求已经有做过的,稍加修改就好。
一上来,当然是要协调各方,定下数据包协议了。
#ifndef I_PACKET_PUBLIC_H
#define I_PACKET_PUBLIC_H
#include <string>
#include <iostream>
using namespace std;
#define MAX_LEN 256
/************接入层数据包头************/
typedef struct packet_header_st
{
int fd;//用于前后端通信即目标客户端fd(服务器用到)
int funcId; // 功能号
//登录包0x01,注册包0x02,找回密码0x03,修改密码0x04
//客户端获取文件列表0x11,上传文件0x12,下载文件0x13,共享文件0x14
//心跳0x21
int optid; // 操作码:请求0x00 和 应答0x01
int usrlenth;// 包体的长度
int packet_seq; //包序号
int packet_sum; //包总数
char srcAddr[6]; //预留
char dstAddr[6]; //预留
int syn; // 判断包头是否正确 0x04
}packet_header_t;
/************接入层数据包尾************/
typedef struct packet_tali_st//包尾,用来验证数据包的完整性
{
int pack_tail;//设置为0x05
}packet_tali_t;
/************数据包报文整体************/
typedef struct packet_all_st
{
packet_header_t head;
char body[packet_header_t::usrlenth];
packet_tali_st tail;
//unsigned len; //要发送的数据有效长度
}packet_all_st;
/************业务层数据包体************/
//客户端登录请求包
typedef struct login
{
int id;
char psw[10]; //密码
}Login_t;
//登录应答包
typedef struct res_login_st
{
int login_ret; //登录结果: 1-登录成功,0-登录失败
}res_login_t;
//客户端注册请求包
typedef struct Register
{
int id; //账号
char tel[12]; //11位手机号
char psw[10]; //密码
}Register_t;
//注册应答包
typedef struct res_register_st
{
int register_ret; //注册结果: 0-注册成功,1-注册失败
}res_register_t;
//客户端修改密码请求包
typedef struct Change_PWD
{
int id; //账号
char psw[10]; //密码
}c_pwd_t;
//改密码应答包
typedef struct res_changepwd_st
{
int change_ret; //改密码结果: 0-改密码成功,1-改密码失败
}res_cpwd_t;
//找回密码请求包
typedef struct findpwd
{
int id;
char tel[12];
}F_Pwd_t;
//找回密码应答包
typedef struct res_findpwd_st
{
int find_ret; //找回结果: 0-找回成功,1-找回失败
char pwd[20]; //密码
}res_fpwd_t;
/************请求业务交易流水请求包************/
//上传文件请求包
typedef struct UpFile
{
int id;
}UpFile_t;
//下载视频请求包
typedef struct Download_File
{
int user_id; //用户ID
char *file_id; //文件名
}Download_t;
//文件列表请求包
typedef struct File_List
{
char *dir_name; //目录名
}FileList_t;
//文件列表应答包
typedef struct Res_VideoList
{
char V[800];
}res_filelist;
#endif // PACKET_BASE_H
但是过早的定下数据包,难免日后不会有修改,之前也确实是深受其扰,直到我接触了PB协议。我觉得这个现象可以得到有效的改善了。
曾经我也很喜欢将数据库的初始化放到主程序中,直到后来去跟我开发N年的表哥吹牛的时候,他说:你数据库的初始化放这里干嘛?嫌开机太快?不怕重复初始化?
哦,好有道理,好像很有道理,确实很有道理。
然后我就改了,我的数据库等需要初始化的外部依赖从此单独初始化!!!
还有,我之所以选择sqlite,而不选择MySQL,甚至于谨慎使用redis,也是跟另一个在游戏公司负责后端开发的学长交流之后,学长跟我说:你这还没开发就把性能限制死了啊,你这数据每次调度都要走两层IO,又不是说什么很大量的数据。你知道在后端开发中,最脆弱的是什么吗?不是高负载的运算,是IO,IO才是最脆弱的。
好,我改。他们都是前辈,我还在象牙塔里,话说也该再约学长出来吃个饭了。
代码太长,只争早夕,整理在这里:FTP文件管理项目(本地云)项目日报(二)
主要是感觉如果以后要加入新的进程,两两相连的服务会有点尴尬。于是想出了这么个主意。
但是不足的地方也很明显,如果中间那个服务给崩了,那一切都免谈了,而且中间那个服务的压力也是很大的(其实大不到哪里去吧,只是个转接的,而且中间不一定要是一个服务啊,可以做一个集群,这个当时倒是没有想到)
中介者服务器(中控)采用accept,边缘服务器全部采用connect,在连接成功后向中控中心汇报自己的情况(fd对应的服务器名是啥,方便通信),奈何我天资愚钝,这个图我想了一晚上。
责任链模式。
大致介绍到这里,我还有很多图,代码也是写完了的,大家可以自行实现一下,然后我们可以一起讨论讨论。