绑定和监听连接
我们利用visual studio创建一个空项目,项目名字为GateServer,然后按照day03的方法配置boost库和jsoncpp配置好后,我们添加一个新的类,名字叫CServer。添加成功后生成的CServer.h和CServer.cpp也会自动加入到项目中。
CServer类构造函数接受一个端口号,创建acceptor接受新到来的链接。
CServer.h包含必要的头文件,以及简化作用域声明
1 |
|
CServer.h中声明acceptor, 以及用于事件循环的上下文iocontext,和构造函数
1 | class CServer:public std::enable_shared_from_this<CServer> |
cpp中实现构造函数如下
1 | CServer::CServer(boost::asio::io_context& ioc, unsigned short& port) :_ioc(ioc), |
接下来我们实现Start函数,用来监听新链接
1 | void CServer::Start() |
Start函数内创建HttpConnection类型智能指针,将_socket内部数据转移给HttpConnection管理,_socket继续用来接受写的链接。
我们创建const.h将文件件和一些作用于声明放在const.h里,这样以后创建的文件包含这个const.h即可,不用写那么多头文件了。
1 |
|
新建HttpConnection类文件,在头文件添加声明
1 |
|
_buffer 用来接受数据
_request 用来解析请求
_response 用来回应客户端
_deadline 用来做定时器判断请求是否超时
实现HttpConnection构造函数
1 | HttpConnection::HttpConnection(tcp::socket socket) |
我们考虑在HttpConnection::Start内部调用http::async_read函数,其源码为
1 | async_read( |
第一个参数为异步可读的数据流,大家可以理解为socket.
第二个参数为一个buffer,用来存储接受的数据,因为http可接受文本,图像,音频等多种资源文件,所以是Dynamic动态类型的buffer。
第三个参数是请求参数,我们一般也要传递能接受多种资源类型的请求参数。
第四个参数为回调函数,接受成功或者失败,都会触发回调函数,我们用lambda表达式就可以了。
我们已经将1,2,3这几个参数写到HttpConnection类的成员声明里了
实现HttpConnection的Start函数
1 | void HttpConnection::Start() |
我们实现HandleReq
1 | void HttpConnection::HandleReq() { |
为了方便我们先实现Get请求的处理,根据请求类型为get调用LogicSystem的HandleGet接口处理get请求,根据处理成功还是失败回应数据包给对方。
我们先实现LogicSystem,采用单例模式,单例基类之前讲解过了
1 |
|
实现LogicSystem单例类
1 |
|
_post_handlers和_get_handlers分别是post请求和get请求的回调函数map,key为路由,value为回调函数。
我们实现RegGet函数,接受路由和回调函数作为参数
1 | void LogicSystem::RegGet(std::string url, HttpHandler handler) { |
在构造函数中实现具体的消息注册
1 | LogicSystem::LogicSystem() { |
为防止互相引用,以及LogicSystem能够成功访问HttpConnection,在LogicSystem.cpp中包含HttpConnection头文件
并且在HttpConnection中添加友元类LogicSystem, 且在HttpConnection.cpp中包含LogicSystem.h文件
1 | bool LogicSystem::HandleGet(std::string path, std::shared_ptr<HttpConnection> con) { |
这样我们在HttpConnection里实现WriteResponse函数
1 | void HttpConnection::WriteResponse() { |
因为http是短链接,所以发送完数据后不需要再监听对方链接,直接断开发送端即可。
另外,http处理请求需要有一个时间约束,发送的数据包不能超时。所以在发送时我们启动一个定时器,收到发送的回调后取消定时器。
我们实现检测超时的函数
1 | void HttpConnection::CheckDeadline() { |
我们在主函数中初始化上下文iocontext以及启动信号监听ctr-c退出事件, 并且启动iocontext服务
1 | int main() |
将十进制的char转为16进制,如果是数字不超过9则加48转为对应的ASCII码的值
如果字符是大于9的,比如AZ, az等则加55,获取到对应字符的ASCII码值
详细的ASCII码表大家可以看这个https://c.biancheng.net/c/ascii/
接下来实现从16进制转为十进制的char的方法
1 | unsigned char FromHex(unsigned char x) |
接下来我们实现url编码工作
1 | std::string UrlEncode(const std::string& str) |
我们先判断str[i]是否为字母或者数字,或者一些简单的下划线,如果是泽直接拼接,否则判断是否为空字符,如果为空则换成’+’拼接。否则就是特殊字符,我们需要将特殊字符转化为’%’和两个十六进制字符拼接。现拼接’%’,再将字符的高四位拼接到strTemp上,最后将低四位拼接到strTemp上。
url解码的工作正好相反
1 | std::string UrlDecode(const std::string& str) |
接下来实现get请求的参数解析, 在HttpConnection里添加两个成员
1 | std::string _get_url; |
参数解析如下
1 | void HttpConnection::PreParseGetParam() { |
HttpConnection::HandleReq函数略作修改
1 | void HttpConnection::HandleReq() { |
我们修改LogicSytem构造函数,在get_test的回调里返回参数给对端
1 | LogicSystem::LogicSystem() { |
在浏览器输入http://localhost:8080/get_test?key1=value1&key2=value2
看到浏览器收到如下图信息,说明我们的get请求逻辑处理完了