Linux -- 基于TCP服务器实现一个简单的电商网站
如何使用TCP协议完成一个服务器然后处理浏览器客户端的请求并返回给浏览器渲染呢?需要分为以下几个步骤:
1.拥有一个TCP服务类,只处理IO请求,不参与实际的应答
2.实现一个HttpRequest类来接受并处理客户端的请求,包括反序列化,提取请求行,请求方法,请求的URL以及Http版本,请求的正文
3.实现一个HttpResponse应答类,需要将应答行,包括服务器HTTP版本号,应答的状态码以及应答状态,还有应答报头以及应答正文一起发送给客户端,然后由客户端渲染并显示页面(这部分工作由浏览器自主完成)。
1.1 封装Socket类
写这个项目时使用了模板的策略方法,因为常用的系统协议由tcp和udp,所以封装了一个基类Socket,然后由对应子类重载不同的虚函数,让不同对象实现不同的协议方法。本文是基于TCP的。所以只实现了TcpServer类。
基类定义了以下几个函数,并实现了一个BulidTcpSocketMethod函数来创建TCP的套接字,当然也可以实现一个BulidUdpSocketMethod,此处没有实现。
TcpSocket类:继承于Socket类,需要实现基类中的几个虚函数,就是简单的创建套接字,绑定,开启监听,send,以及recv,还实现了一个Acceper与客户端连接的函数,这个函数在创建连接以后会将连接者的信息带出,以及返回一个SockPtr类型的指针对象, SockPtr具体是std::shared_ptr<Socket>类型,这个指针对象包含了新创建的连接的sockfd,方便后续于客户端进行通信。
1.2实现服务器封装TcpServer类
通过刚刚封装的子类TcpSocket创建一个属于TCP的_listensock(std::make_unique<TcpSocket>()),然后调用BulidTcpSocketMethod初始化套接字,接着实现loop,函数,使服务器一直处于循环,以多进程的方式与不同的客户端进行连接,并回调具体的与客户端交互的函数,服务器只处理IO
1.3实现HttpServer类处理具体的客户端请求与应答
这个类需要具体实现与客户端的交互,但是只作为上层框架调用,再具体的实现需要由HttpRequest和HttpResponse来完成。首先由外部传入的端口号构造了一个成员变量_tsvr,然后完成一个Start函数,其中调用的TcpServer中的Init与loop函数,将处理请求与应答的函数HandlerHttpRequest传递给服务器。在HandlerHttpRequest中先收到客户端的请求,然后对客户端请求进行反序列化,判断请求是否带参构建不同的响应信息,如果带参需要调用外部的不同请求方法。
2.1具体的HttpRequest类
请求类的主要功能是将请求信息进行反序列化然后保存起来,其中需要的一些成员变量如下
我们需要通过Deserialize进行反序列化,解析客户端发来的信息
其中首先需要提前请求行ParseOneLine,然后提前请求字段,这里将提取一行单独拆分处理
然后使用循环ParseHeadLine读取报头信息并保存在head_lines数组中,接着将数组中的每一行报头信息进行分割成" str ":" str "的格式
接着填写空白行,最后剩下的request_str只有正文信息了,因为ParseOneLine在读取每一行以后会将该行删除。最后分析请求中是否带有参数,如果请求方法是POST证明请求行不带参,参数一般在正文内容中,所以这里将需要访问的文件以及参数再用两个变量储存,方便标识。如果请求方法是GET,并且uri中带有 ? 证明uri带参,需要单独提取并在请求行中删除参数。
需要暴露出来给响应类的一些接口函数
3.1具体的HttpResponse类
与请求类类似,也需要一些响应字段以及发送的部分
我们需要根据请求的信息来进行构建响应字段,如果请求中的uri的末尾为"/",说明请求的是该目录的首页,我们需要加上外部目录与首页文件路径。然后通过GetContent获取请求需要的文件信息,接着判断文件是否为空,为空则说明访问的文件不存在,需要返回错误页面,并设置404状态码,如果不为空则将状态码设置为200表示正常获取,再根据状态码设置状态描述,最后构建响应的一些报头。
完成了以上步骤以后,就到我们最上层的调用了。
看到这里有的同学可能会很晕,因为已经封装了三层,我来讲解一下服务器的工作流程:首先创建一个HttpServer的指针对象,然后通过Reguster注册请求方法,将LoginOregister注册进去,再通过start启动服务器,服务器中的_tsvr对象会执行Init初始化,将HandlerHttpRequest方法传递给TcpServer中的_handler,然后通过loop启动服务器,在服务器循环的内部先与客户端连接,接着将新创建SockPtr tsv_ptr交给多进程去回调_handler方法,此时代码执行到HandlerHttpRequest内部,接受来自客户端的请求,然后对客户端请求进行反序列化,判断请求是否带参数,如果带参数需要根据uri执行外部注册进_route的方法,如果不带参数那就执行正常的构建响应信息,最后将响应信息序列化并发送给客户端,在我们客户端收到了服务器发来的数据后会对正文部分进行渲染,也就看到了我们的网页。
实际的效果:服务器能够正确收到客户端请求并处理,然后正确返回给客户端。
tips:前端的一些页面由于主播并不会,使用的ai生成大家觉得页面不够完美可以让ai生成一个更好的,ai写前端还是很厉害的,另外需要完整代码可以查看我的gitee中的文件HttpServer-V3,代码其中有一些LOG类,InetAddr是我自己封装的类,都在同一个文件夹中可以找到。
感谢观看。