QT聊天项目DAY05

1.Git拉取项目

编译一下,通过了,虽然会出现报错,但是经常写虚幻C++的人应该对此见怪不怪了吧

2. 实现main.cpp

io_context 所有的异步操作(如网络,信号) 都通过它来调度

注册信号处理器,监听系统终止信号

创建服务器对象实时的监听8080端口是否有客户端请求连接,如果有客户端请求连接,将该连接实例化成一个对象的方式来管理连接中的处理,服务器本身继续监听8080端口是否有新的客户端连接

在连接对象中来处理客户端的读事件

一个端口能够支持成千上万个并发连接

#include <iostream>

#include "CServer.h"
#include "HttpConnection.h"
#include "LogicSystem.h"

int main()
{
    try 
    {
        unsigned short port = static_cast<unsigned short>(8080);                            // 指定服务器监听的端口号
        net::io_context ioc(1);                                                             // 异步操作的引擎,所有事件循环都通过他来操作,单线程
        boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);                              // 信号处理,用于退出程序
        // 绑定信号的回调函数
        signals.async_wait([&ioc](const boost::system::error_code& error, int sognalNumber)
            {
                if (error)
                    return;
                ioc.stop();
            });

        std::shared_ptr<CServer> server = std::make_shared<CServer> (ioc, port);
        server->Start();
        ioc.run();
    }
    catch (std::exception& e) 
    {
        std::cerr << "Exception: " << e.what() << "\n";
        return EXIT_FAILURE;
    }
}

3.编译并且测试服务器是否有效

3.1 测试连接是否有效

输入localhost:8080/getTest

3.2 查询出错原因

crtl + T 文本形式搜索

如果当前对象不是通过std::make_shared<CServer>创建的,调用shared_from_this()就会抛出这个异常

将上述代码修改为

发现即便修改了还是没用,就算是不继承std::enable_shared_from_this<HttpConnection>还是会报错,对不起了,我自己来管理指针,将全部的智能指针删除,直接用裸指针,在使用裸指针时要

自己来管理if判断一下,判断是否为空

全局使用别的对象指针的只有这一个地方

4.解析URL

将一个十六进制的字符转换成对应的数字(0~15)

unsigned char HttpConnection::FromHex(unsigned char c)
{
	unsigned char y;
	if(c >= 'A' && c <= 'Z')
		y = c - 'A' + 10;
	else if(c >= 'a' && c <= 'z')
		y = c - 'a' + 10;
	else
		y = c - '0';
	return y;
}

把一个数字(0~15)转换为它对应的十六进制字符

unsigned char HttpConnection::ToHex(unsigned char c)
{
	return c > 9 ? c + 55 : c + 48;
}

将输入字符串转换成URL安全的格式

string HttpConnection::UrlEncode(const string& str)
{
	string strTemp = "";
	size_t len = str.length();
	for (size_t i = 0; i < len; i++)
	{
		if (isalnum((unsigned char)str[i])
			|| str[i] == '-'
			|| str[i] == '_'
			|| str[i] == '.'
			|| str[i] == '~')
		{
			strTemp += str[i];
		}
		else
		{
			strTemp += '%';
			strTemp += ToHex((unsigned char)str[i] >> 4);
			strTemp += ToHex((unsigned char)str[i] & 0x0F);
		}
	}
	return strTemp;
}

将URL转换成字符串

string HttpConnection::UrlDecode(const string& str)
{
	std::string strTemp = "";
	size_t length = str.length();
	for (size_t i = 0; i < length; i++)
	{
		//还原+为空
		if (str[i] == '+') strTemp += ' ';
		//遇到%将后面的两个字符从16进制转为char再拼接
		else if (str[i] == '%')
		{
			assert(i + 2 < length);
			unsigned char high = FromHex((unsigned char)str[++i]);
			unsigned char low = FromHex((unsigned char)str[++i]);
			strTemp += high * 16 + low;
		}
		else strTemp += str[i];
	}
	return strTemp;
}

将URL参数解析成map,方便后面直接用键取值

/* 将URL参数解析成map,方便后面直接用键取值 */
void HttpConnection::PreParseGetParam()
{
	// 提取 URI  
	auto uri = _request.target();
	// 查找查询字符串的开始位置(即 '?' 的位置)  
	auto query_pos = uri.find('?');
	if (query_pos == std::string::npos) {
		_getUrl = uri;
		return;
	}

	_getUrl = uri.substr(0, query_pos);
	std::string query_string = uri.substr(query_pos + 1);
	std::string key;
	std::string value;
	size_t pos = 0;
	while ((pos = query_string.find('&')) != std::string::npos) {
		auto pair = query_string.substr(0, pos);
		size_t eq_pos = pair.find('=');
		if (eq_pos != std::string::npos) {
			key = UrlDecode(pair.substr(0, eq_pos)); // 假设有 url_decode 函数来处理URL解码  
			value = UrlDecode(pair.substr(eq_pos + 1));
			_getParams[key] = value;
		}
		query_string.erase(0, pos + 1);
	}
	// 处理最后一个参数对(如果没有 & 分隔符)  
	if (!query_string.empty()) {
		size_t eq_pos = query_string.find('=');
		if (eq_pos != std::string::npos) {
			key = UrlDecode(query_string.substr(0, eq_pos));
			value = UrlDecode(query_string.substr(eq_pos + 1));
			_getParams[key] = value;
		}
	}
}

这些东西根本记不住,也没必要,问一下AI就行了,毕竟这种接口给它当成原子操作用就完事了

5.处理请求时解析URL

5.1 找到处理连接客户端请求的函数

看这个数据是什么时候获取的,应该是当检测到数据到来时会自动的将数据存储到_request中

所以如果URL是get请求就解析他

/* 处理客户端的请求数据 */
void HttpConnection::HandleRequest()
{
	_response.version(_request.version());
	_response.keep_alive(false);													// 处理完该请求后断开连接
	if (_request.method() == http::verb::get)										// 如果是Http的Get请求
	{
		PreParseGetParam();															// 解析客户端发来的URL请求
		bool IsSucceed = LogicSystem::GetInstance()->HandleGet(_getUrl, this);
		if (!IsSucceed)
		{
			_response.result(http::status::not_found);
			_response.set(http::field::content_type, "text/plain");
			beast::ostream(_response.body()) << "url not found\r\n";
			WriteResponse();
			return;
		}

		_response.result(http::status::ok);
		_response.set(http::field::server, "GateServer");
		WriteResponse();
		return;
	}
}

将解析出来的数据返回出去

LogicSystem::LogicSystem()
{
	RegisterGet("/getTest", [](HttpConnection* connection)
		{
			if (connection)
			{
				beast::ostream(connection->_response.body()) << "receive getTest request";	// 向 Http响应体中写入内容

				for (auto content : connection->_getParams)
				{
					beast::ostream(connection->_response.body()) << content.first << " : " << content.second << std::endl;	// 向 Http响应体中写入内容
				}
			}
			else
			{
				std::cout << "connection is null" << std::endl;
			}
		});
}

6. 注册客户端Post请求

void RegisterPost(std::string, HttpHandler);														// 注册客户端Post请求

void LogicSystem::RegisterPost(std::string url, HttpHandler handler)
{
	_postHandlers[url] = handler;
}

6.1 添加全局头文件

#ifndef GLOBALHEAD_H
#define GLOBALHEAD_H

#include <boost/beast/http.hpp>
#include <boost/beast.hpp>
#include <boost/asio.hpp>

#include <memory>

#include <iostream>

#include "Singletion.h"
#include <functional>
#include <map>
#include <unordered_map>
#include <string>

#include <json/json.h>
#include <json/value.h>
#include <json/writer.h>
#include <json/reader.h>

namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = boost::asio::ip::tcp;

enum ErrorCodes
{
	SUCCESS = 0,
	ERROR_JSON = 1001,
	RPC_FAILED = 1002,

	ERROR_JSON_KEY_EMAIL_LACK = 1003,
};

#endif // GLOBALHEAD_H

6.2 绑定Post请求URL对应的回调函数

在这里需要Json来解析Post请求,首先获取Http请求中的内容,然后用jsonCpp这个第三方库的API去解析这个库,再设置服务器回送响应的内容,设置响应头为json文本,判断有没有解析成功,如果没解析成功向响应中添加没有解析成功的错误。如果解析成功了需要判断这个结果中是否包含有email,如果不包含向响应中添加Eamil确实的错误,最后设置解析成功的响应

	// 注册Post请求URL对应的回调函数
	RegisterPost("/getVarifycode", [](HttpConnection* connection)
		{
			if (connection)
			{
				auto bodyStr = boost::beast::buffers_to_string(connection->_request.body());		// 获取 Http请求体中的内容
				cout << "receive body is " << bodyStr << endl;
				connection->_response.set(http::field::content_type, "text/json");					// 设置 Http响应头中的 content-type
				Json::Value jsonResonse;															// 响应用的Json
				Json::Value jsonResult;																// 请求体解析出来的Json
				Json::Reader reader;																// Json解析器

				bool parseSuccess = reader.parse(bodyStr, jsonResult);								// 将请求体解析为Json
				if (!parseSuccess)
				{
					cout << "parse json failed" << endl;
					jsonResonse["error"] = ErrorCodes::ERROR_JSON;									// 设置响应的错误码
					string jsonStr = jsonResonse.toStyledString();
					beast::ostream(connection->_response.body()) << jsonStr;						// 向 Http响应体中写入错误码内容
					return;
				}

				/* 解析成功了,判断是否有想要的key */
				if (!jsonResult.isMember("email"))
				{
					cout << "email not found in json" << endl;
					jsonResonse["error"] = ErrorCodes::ERROR_JSON_KEY_EMAIL_LACK;
					string jsonStr = jsonResonse.toStyledString();
					beast::ostream(connection->_response.body()) << jsonStr;
					return;
				}

				auto email = jsonResonse["email"].asString();										// 获取解析出来的Email
				cout << "email is " << email << endl;

				jsonResonse["error"] = 0;
				jsonResonse["email"] = jsonResult["email"];
				string jsonStr = jsonResonse.toStyledString();
				beast::ostream(connection->_response.body()) << jsonStr;							// 向 Http响应体中写入Json内容
				return;
			}
			else
			{
				std::cout << "connection is null" << std::endl;
			}
		});

6.3 处理Post请求的函数

bool HandlePost(std::string, HttpConnection*);														// 处理客户端Post请求
bool LogicSystem::HandlePost(std::string url, HttpConnection* connection)
{
	if (_postHandlers.find(url) == _postHandlers.end())
		return false;
	_postHandlers[url](connection);
	return true;
}

6.4 在连接处理客户端请求时添加Post的判断

	// 如果是Http的Post请求
	if (_request.method() == http::verb::post)										
	{
		bool IsSucceed = LogicSystem::GetInstance()->HandleGet(_request.target(), this);
		if (!IsSucceed)
		{
			_response.result(http::status::not_found);
			_response.set(http::field::content_type, "text/plain");
			beast::ostream(_response.body()) << "url not found\r\n";
			WriteResponse();
			return;
		}

		_response.result(http::status::ok);
		_response.set(http::field::server, "GateServer");
		WriteResponse();
		return;
	}

7. 编译并测试

7.1 编译

参数不匹配,直接丢给AI,问正确的参数是什么?

更改为下列参数

编译成功

7.2 测试Post请求

下载PostMan,需要魔法哦

1. 创建空白分支

2. 填充要发送的客户端请求

URL没有被发现

离谱嗷,更改一下key为下面这一个

还是报错,又回头看了一下代码,复制的get请求,头晕眼花了忘记改了

将之前更改的Key再改回去,跑通了,上传Gitee

8. 上传Gitee

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值