【Mudo】实战项目之测试模块

前言

在前几篇文章中,主要对项目的框架和代码的实现角度介绍了此项目,但是骡子是马还得牵出来遛一遛,本篇文章主要是介绍一下如何遛你的码,即对代码进行功能性的测试,看代码是否能够稳定运行,话不多说,马上开始。

正文

1. 编译

首先得保证你写的代码从语法和逻辑上大致都没有问题才能进行跑。当进行编译不通过时,根据编译器提示的错误慢慢梳理即可;当运行时程序崩溃时,可根据打印日志信息,对根据蛛丝马迹找到线索,解开bug。如下是日志宏的介绍,方便运行时进行查看出错的可能位置,进而追踪和定位Bug,然后才是分析和解决Bug。

顺便分享一下之前梳理的系统日志的介绍:

在这里插入图片描述

以及关于代码的处理流程:

在这里插入图片描述

相关接口:

#include <time.h>
time_t time(time_t *tloc);
/*
参数:输出型参数,获取时间戳。
返回值:获取时间戳
*/
struct tm *localtime(const time_t *timep);
struct tm {
    int tm_sec;    /* Seconds (0-60) */
    int tm_min;    /* Minutes (0-59) */
    int tm_hour;   /* Hours (0-23) */
    int tm_mday;   /* Day of the month (1-31) */
    int tm_mon;    /* Month (0-11) */
    int tm_year;   /* Year - 1900 */
    int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
    int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
    int tm_isdst;  /* Daylight saving time */
};
/*
参数:输出型参数,获取时间戳。
返回值:转成存放时间类型的结构体对象。
*/
size_t strftime(char *s, size_t max, const char *format,const struct tm *tm);
/*
参数
	1.输出型参数,存放struct tm转为将字符串的结果。
	2.输出结果的最大长度。
	3.输入型参数,时间类型的结构体对象。

返回值:
	1.成功,转成字符串的实际长度。
	2.失败返回0,注意返回0不一定意味着失败。
*/
#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);
/*
参数
	1.文件流指针,比如stdout,stdin,stderr
	2.格式化处理的字符串。
	3.可变参数列表,在内部可变参数作为参数传递时需要用到宏__VA_ARGS__,且前面需加上##说明其为参数名的一部分。
返回值:
	1.成功,转成字符串的实际长度。
	2.失败返回0,注意返回0不一定意味着失败。
*/

实现:


//时间函数
#include<ctime>
//输入输出
#include<cstdio>
//字符串
#include<cstring>
//错误码
#include<errno.h>
//设日志等级为信息,调试, 警告, 错误, 值越大等级越高。
enum level
{
    INFOR,
    DEBUG,
    WARING,
    ERROR
};
/*
思路
  1.获取当前时间戳。
  2.将时间戳转化为struct tm类型结构。
  3.将struct tm类型结构转换为标准形式的字符串存储起来。
  4.打印信息,包含时间,所在文件名,文件的行数,日志等级和要所查看的可变参数。
*/
#define LOG(lev,format,...) do{\
        time_t t = time(0);\
        struct tm* ltm= localtime(&t);\
        char t_str[32]={0};\
        strftime(t_str,sizeof(t_str) - 1,"%H:%M:%S",ltm);\
        fprintf(stdout,"[%s,%s,%d,%d]:"#format"\n",t_str,__FILE__,__LINE__,lev,##__VA_ARGS__);\
        }while(0)
#define INF_LOG(format,...) LOG(INFOR,format,##__VA_ARGS__);
#define DEBUG_LOG(format,...) LOG(DEBUG,format,##__VA_ARGS__);
#define ERROR_LOG(format,...) LOG(ERROR,format,##__VA_ARGS__);
#define WARING_LOG(format,...) LOG(WARING,format,##__VA_ARGS__);

2. 运行

1. 服务器

//设置为你服务器的资源的绝对根目录即可,使用pwd命令cv。
std::string sdir = "/home/ubuntu/MudoProject/practical-projects/imitate_mudo_concurrent_server/source/Http/wwwroot/";
void Print(const HttpRequest& req,HttpResponse* rsp)
{
    //组装Http协议信息
    //版本号 状态码 状态表示\r\n
    std::stringstream stm;
    stm << "------------------------------------------------------------------" << "\r\n";
    stm << req._version << " " << req._method << " " << req._source_dir << "\r\n";
    //头部信息
    for(auto& kv : req._headers)
    {
        stm << kv.first << ": " << kv.second << "\r\n";
    }
    //参数
    for(auto& kv : req._parameter)
    {
        stm << kv.first << ": " << kv.second << "\r\n";
    }
    stm << "\r\n";
    //头部信息
    stm << req._body;
    stm << "\r\n";
    stm << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << "\r\n";
    //设置响应正文
    rsp->SetContent(stm.str(),"text/plain");
}
//Get,,Post,Delete,此动态处理本项目没有相应的业务需求因此将数据打包返回。
//Put,可以进行简单的测试,即上传大文件。
void Get(const HttpRequest& req,HttpResponse* rsp)
{
    //将数据按原样为正文进行返回。
    Print(req,rsp);
}
void Put(const HttpRequest& req,HttpResponse* rsp)
{
    Util::WriteFile(sdir + "bf.txt",req._body);
}
void Post(const HttpRequest& req,HttpResponse* rsp)
{
    Print(req,rsp);
}
void Delete(const HttpRequest& req,HttpResponse* rsp)
{
    Print(req,rsp);
}
int main()
{
    //创建服务器,使用默认端口号8000
    HttpServer svr; 
    //设置功能性请求。
    svr.SetHander("GET","/get",Get);
    svr.SetHander("POST","/post",Post);
    svr.SetHander("PUT","/put",Put);
    svr.SetHander("DELETE","/delete",Delete);
    //启动服务器,内部默认是4个线程
    svr.Start();
    return 0;
}

当进行简单的功能测试时,可以下载PostMan工具,模拟Http协议报文进行发送,验证对应的功能和服务器的运行状况。

2. 客户端

在实战项目的开篇文章当中,通过浏览器测试获取主页和返回错误界面,本质是一种前端网页的编写,博主找的是现成的,因为博主是C++后端方向的,只是懂怎么用,怎么编写等学有余力的时候,再进行学习相应的前端知识并分享给大家,在这是算是给自己挖一个坑,如下主要是通过客户端和相应的测试工具进行测试的。

传输大文件

  • 生成一个大文件:
dd if=/dev/zero of=bf.txt bs=1M count=100
//创建一个100MB的大文件。
dd
/*
dd命令
参数:
    if,inputfile,即设置输入的文件,通常为/dev/zero设备中的垃圾输入流,可以提取无尽的0。
    of,outputfile,设置输出文件的文件名。
    bs,blocksize,块大小。
    count,块的数量。
    bs * count,即为创建文件的大小。
*/

说明:我的云服务器是一核两G的,只有2G内存,除去系统占用的之外,极限最大传输的文件可能也就1G左右,条件有限,有条件的同学可以使用虚拟机分配4/6G的内存,进行测试。


实现:

#include"/home/ubuntu/MudoProject/practical-projects/imitate_mudo_concurrent_server/include/mudo.h"
#include"/home/ubuntu/MudoProject/practical-projects/imitate_mudo_concurrent_server/include/http.h"
//进行长连接分批发送数据处理的测试。
int main()
{
    CSocket skt("49.232.138.58");
    skt.Connect();
    //非活跃,且为长连接持续时长为30秒
    std::string reqline = "PUT /put HTTP/1.1\r\n";
    std::string headers = "Connection: keep-alive\r\n";
    std::string body;
    Util::ReadFile("./bf.txt",&body);
    headers += "Content-Length: " + std::to_string(body.size()) + "\r\n\r\n";
    std::string data = reqline + headers + body;
    skt.Send(data);
    while(1) sleep(1);
    skt.Close();
    return 0;
}

传输如果有误,则根据日志信息慢慢测就完了,没问题,则进行数据验证。

  • 命令
	md5sum [文件名]

看服务器传输成功的文件数据的MD5值是否与客户端的文件的Md5相同,如果相同则验证无误。

长连接

  • 非活跃连接
#include"/home/ubuntu/MudoProject/practical-projects/imitate_mudo_concurrent_server/include/mudo.h"
//进行长连接以及非活跃连接的测试。
int main()
{
    CSocket skt("49.232.138.58");//这是我云服务器的公网ip,查看你服务器的ip设置即可。
    skt.Connect();
    //非活跃,且为长连接持续时长为30秒,30s是此项目中设置的,自己也可设置。
    std::string data = "GET /get HTTP/1.1\r\nConnection: keep-alive\r\n\r\n";
    skt.Send(data);
    char buffer[65536] = {0};
    ssize_t ret = skt.Recv(buffer,sizeof(buffer) - 1);
    while(1) sleep(1);
    return 0;
}

说明:设置并查看服务器的日志信息,先运行服务器,运行起来之后,如果客户端也运行,然后发送一次连接30s后,服务器日志连接出现销毁信息,说明测试成功。

  • 分批发送数据
#include"/home/ubuntu/MudoProject/practical-projects/imitate_mudo_concurrent_server/include/mudo.h"
//进行长连接分批发送数据处理的测试。
int main()
{
    CSocket skt("49.232.138.58");
    skt.Connect();
    //非活跃,且为长连接持续时长为30秒
    std::string data = "GET /get HTTP/1.1\r\nConnection: keep-alive\r\nContent-Length: 100\r\n\r\n";
    //先只发送请求,后面是只发送数据
    skt.Send(data);
    //发送数据10次,每次发送数据的长度为10,最终于Content-Length: 100相照应。
    int cnt = 10;
    while(cnt--)
    {
        skt.Send("5205205205");
    }
    char buffer[65536] = {0};
    //进行阻塞接收
    ssize_t ret = skt.Recv(buffer,sizeof(buffer) - 1);
    if(ret == -1)
    {
        INF_LOG("reason:%s",strerror(errno));
    }
    else
    {
        printf("%s",buffer);
    }
    skt.Close();
    return 0;
}

如果正常的收到消息,则说明测试无误,如果没有则看看日志信息,并查看连接测试部分的代码哪出问题了,Debug要耐心!

短连接

#include"/home/ubuntu/MudoProject/practical-projects/imitate_mudo_concurrent_server/include/mudo.h"
//进行短连接的测试。
int main()
{
    INF_LOG("Short Connetion Test.");
    CSocket skt("49.232.138.58");
    skt.Connect();
    //短连接直接关闭
    for(int i = 0; i < 3; i++)
    {
        std::string data = "GET /get HTTP/1.1\r\n\r\n";
        ssize_t ret =  skt.Send(data);
        if(ret == -1 || ret == 0)
        {
            INF_LOG("reason:%s",strerror(errno));
        }
        char buffer[65536] = {0};
        ret = skt.Recv(buffer,sizeof(buffer) - 1);
        if(ret == -1)
        {
            INF_LOG("reason:%s",strerror(errno));
        }
        else
        {
            printf("%s",buffer);
        }
    }
    while(1) sleep(1);
    skt.Close();
    return 0;
}

短连接只需发送一次消息,查看响应的头部是否有Connection: close,如果有则无误,如果没有,则再看看服务器是否把连接关闭了,如果都没有则说明连接模块出问题了,慢慢看日志调吧兄弟。。

压力测试

说明:这是博主从网上找的整理过后放到我的Gitee仓库中便于查看,整理了文档,感兴趣的同学可进一步学习~

  • 测试:在你的服务器上使用如下命令进行测试,博主的服务器配置太低只能每秒处理1000个客户端,可以给虚拟机分配4-8G的内存,在上面跑一跑。

  • 标准:并发量,可以同时处理多少客户端的请求而不会出现连接失败。Qps,即每秒钟处理的包的数量。

  • 示例:如下是博主下的Kali虚拟机,用来简单的测一下,1000的并发量还是轻轻松松的。
    在这里插入图片描述
    补充:本项目还没有经过7*24小时的测试,因此还是有一些小bug的,因此不太稳定,因此供大家进行参考还是可以的,与此同时博主在之后会找时间提高所写代码的稳定性的,又挖了一个坑^ _ ^。

尾序

 仿mudo库实现高并发服务器组件项目到这里就到尾声了,博主在后续将开启MySQL的分享,敬请期待!我是舜华期待与你的下一次相遇!我们下篇文章再见了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值