广告
返回顶部
首页 > 资讯 > 服务器 >怎么实现一个Http服务器
  • 813
分享到

怎么实现一个Http服务器

2023-06-04 22:06:06 813人浏览 安东尼
摘要

这期内容当中小编将会给大家带来有关怎么实现一个Http服务器,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。说到http协议和http请求,很多人都知道,但是他们真的“知道”吗?我面试过很多求职者,一说到h

这期内容当中小编将会给大家带来有关怎么实现一个Http服务器,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

说到http协议和http请求,很多人都知道,但是他们真的“知道”吗?我面试过很多求职者,一说到http协议,他们能滔滔不绝,然后我问他http协议的具体格式是啥样子的?很多人不清楚,不清楚就不清楚吧,他甚至能将http协议的头扯到html文档头部。当我问http GET和POST请求的时候,GET请求是什么形式一般人都可以答出来,但是POST请求的数据放在哪里,服务器如何识别和解析这些POST数据,很多人又说不清道不明了。当说到http服务器时,很多人离开了apache、Nginx这样现成的http server之外,自己实现一个http服务器无从下手,如果实际应用场景有需要使用到一些简单http请求时,使用apache、Nginx这样重量级的http服务器程序实在劳师动众,你可以尝试自己实现一个简单的。

上面提到的问题,如果您不能清晰地回答出来,可以阅读一下这篇文章,这篇文章在不仅介绍http的格式,同时带领大家从零实现一个简单的http服务器程序。

http协议介绍

http协议是应用层协议,一般建立在tcp协议的基础之上(当然你的实现非要基于udp也是可以的),也就是说http协议的数据收发是通过tcp协议的。

http协议也分为head和body两部分,但是我们一般说的html中的和标记不是http协议的头和身体,它们都是http协议的body部分。

怎么实现一个Http服务器

那么http协议的头到底长啥样子呢?我们来介绍一下http协议吧。

http协议的格式如下:

1GET或POST 请求的url路径(一般是去掉域名的路径) HTTP协议版本号\r\n 2字段1名: 字段1值\r\n 3字段2名: 字段2值\r\n 4     … 5字段n名 : 字段n值\r\n 6\r\n 7http协议包体内容

也就是说http协议由两部分组成:包头和包体,包头与包体之间使用一个\r\n分割,由于http协议包头的每一行都是以\r\n结束,所以http协议包头一般以\r\n\r\n结束。

举个例子,比如我们在浏览器中请求http://www.hootina.org/index_2013.PHP这个网址,这是一个典型的GET方法,浏览器组装的http数据包格式如下:

GET /index_2013.php HTTP/1.1\r\n 2Host: www.hootina.org\r\n 3Connection: keep-alive\r\n 4Upgrade-Insecure-Requests: 1\r\n 5User-Agent: Mozilla/5.0 (windows NT 6.1; Win64; x64) AppleWEBKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36\r\n 6Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,**;q=0.8\r\n 7Accept-Encoding: gzip, deflate\r\n 8Accept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\n 9\r\n

对比一下,你现在知道http协议的GET参数放在协议包的什么位置了吧。

那么POST的数据放在什么位置呢?我们再12306网站https://kyfw.12306.cn/otn/login/init中登陆输入用户名和密码:

怎么实现一个Http服务器

然后发现浏览器以POST方式组装了http协议包发送了我们的用户名、密码和其他一些信息,组装的包格式如下:

POST /passport/web/login HTTP/1.1\r\n  2Host: kyfw.12306.cn\r\n  3Connection: keep-alive\r\n  4Content-Length: 55\r\n  5Accept: application/JSON, text/javascript, * 24    //检查是否以\r\n\r\n结束,如果不是说明包头不完整,退出 25    string end = inbuf.substr(inbuf.length() - 4); 26    if (end != "\r\n\r\n") 27        return; 28 29    //以\r\n分割每一行 30    std::vector<string> lines; 31    StringUtil::Split(inbuf, lines, "\r\n"); 32    if (lines.size() < 1 || lines[0].empty()) 33    { 34        conn->forceClose(); 35        return; 36    } 37 38    std::vector<string> chunk; 39    StringUtil::Split(lines[0], chunk, " "); 40    //chunk中至少有三个字符串:GET+url+HTTP版本号 41    if (chunk.size() < 3) 42    { 43        conn->forceClose(); 44        return; 45    } 46 47    LOG_INFO << "url: " << chunk[1] << " from " << conn->peerAddress().toIpPort(); 48    //inbuf = /reGISter.do?p={%22username%22:%20%2213917043329%22,%20%22nickname%22:%20%22balloon%22,%20%22passWord%22:%20%22123%22} 49    std::vector<string> part; 50    //通过?分割成前后两端,前面是url,后面是参数 51    StringUtil::Split(chunk[1], part, "?"); 52    //chunk中至少有三个字符串:GET+url+HTTP版本号 53    if (part.size() < 2) 54    { 55        conn->forceClose(); 56        return; 57    } 58 59    string url = part[0]; 60    string param = part[1].substr(2); 61 62    if (!Process(conn, url, param)) 63    { 64        LOG_ERROR << "handle http request error, from:" << conn->peerAddress().toIpPort() << ", request: " << pBuffer->retrieveAllAsString(); 65    } 66 67    //短连接,处理完关闭连接 68    conn->forceClose(); 69}

代码注释都写的很清楚,我们先利用\r\n分割得到每一行,其中第一行的数据是:

GET /register.do?p={%22username%22:%20%2213917043329%22,%20%22nickname%22:%20%22balloon%22,%20%22password%22:%20%22123%22} HTTP/1.1

其中%22是双引号的url转码形式,%20是空格的url转码形式,然后我们根据空格分成三段,其中第二段就是我们的网址和参数:

/register.do?p={%22username%22:%20%2213917043329%22,%20%22nickname%22:%20%22balloon%22,%20%22password%22:%20%22123%22}

然后我们根据网址与参数之间的问号将这个分成两段:第一段是网址,第二段是参数:

1bool HttpSession::Process(const std::shared_ptr<TcpConnection>& conn, const std::string& url, const std::string& param)  2{  3    if (url.empty())  4        return false;  5  6    if (url == "/register.do")  7    {  8        OnRegisterResponse(param, conn);  9    } 10    else if (url == "/login.do") 11    { 12        OnLoginResponse(param, conn); 13    } 14    else if (url == "/getfriendlist.do") 15    { 16 17    } 18    else if (url == "/getgroupmembers.do") 19    { 20 21    } 22    else 23        return false; 24 25 26    return true; 27}

然后我们根据url匹配网址,如果是注册请求,会走注册处理逻辑:

void HttpSession::OnRegisterResponse(const std::string& data, const std::shared_ptr<TcpConnection>& conn)  2{  3    string retData;  4    string decodeData;  5    URLEncodeUtil::Decode(data, decodeData);  6    BussinessLogic::RegisterUser(decodeData, conn, false, retData);  7    if (!retData.empty())  8    {  9        std::string response; 10        URLEncodeUtil::Encode(retData, response); 11        MakeupResponse(retData, response); 12        conn->send(response); 13 14        LOG_INFO << "Response to client: cmd=msg_type_register" << ", data=" << retData << conn->peerAddress().toIpPort();; 15    } 16}

注册结果放在retData中,为了发给客户端,我们将结果中的特殊字符如双引号转码,如返回结果是:

{"code":0, "msg":"ok"}

会被转码成:

{%22code%22:0,%20%22msg%22:%22ok%22}

然后,将数据组装成http协议发给客户端,给客户端的应答协议与http请求协议有一点点差别,就是将请求的url路径换成所谓的http响应码,如200表示应答正常返回、404页面不存在。应答协议格式如下:

GET或POST 响应码 HTTP协议版本号\r\n 2字段1名: 字段1值\r\n 3字段2名: 字段2值\r\n 4     … 5字段n名 : 字段n值\r\n 6\r\n 7http协议包体内容

举个例子如:

HTTP/1.1 200 OK\r\n Content-Type: text/html\r\n Content-Length:42\r\n \r\n {%22code%22:%200,%20%22msg%22:%20%22ok%22}

注意,包头中的Content-Length长度必须正好是包体{%22code%22:%200,%20%22msg%22:%20%22ok%22}的长度,这里是42。这也符合我们浏览器的返回结果:

怎么实现一个Http服务器

当然,需要注意的是,我们一般说http连接一般是短连接,这里我们也实现了这个功能(看上面的代码:conn->forceClose();),不管一个http请求是否成功,服务器处理后立马就关闭连接。

当然,这里还有一些没处理好的地方,如果你仔细观察上面的代码就会发现这个问题,就是不满足一个http包头时的处理,如果某个客户端(不是使用浏览器)通过程序模拟了一个连接请求,但是迟迟不发含有\r\n\r\n的数据,这路连接将会一直占用。我们可以判断收到的数据长度,防止别有用心的客户端给我们的服务器乱发数据。我们假定,我们能处理的最大url长度是2048,如果用户发送的数据累积不含\r\n\r\n,且超过2048个,我们认为连接非法,将连接断开。代码修改成如下形式:

void HttpSession::OnRead(const std::shared_ptr<TcpConnection>& conn, Buffer* pBuffer, Timestamp receivTime) {     //LOG_INFO << "Recv a http request from " << conn->peerAddress().toIpPort();      string inbuf;     //先把所有数据都取出来     inbuf.append(pBuffer->peek(), pBuffer->readableBytes());     //因为一个http包头的数据至少\r\n\r\n,所以大于4个字符     //小于等于4个字符,说明数据未收完,退出,等待网络底层接着收取     if (inbuf.length() <= 4)         return;      //我们收到的GET请求数据包一般格式如下:          //检查是否以\r\n\r\n结束,如果不是说明包头不完整,退出     string end = inbuf.substr(inbuf.length() - 4);     if (end != "\r\n\r\n")         return;     //超过2048个字符,且不含\r\n\r\n,我们认为是非法请求     else if (inbuf.length() >= MAX_URL_LENGTH)     {         conn->forceClose();         return;     }      //以\r\n分割每一行     std::vector<string> lines;     StringUtil::Split(inbuf, lines, "\r\n");     if (lines.size() < 1 || lines[0].empty())     {         conn->forceClose();         return;     }      std::vector<string> chunk;     StringUtil::Split(lines[0], chunk, " ");     //chunk中至少有三个字符串:GET+url+HTTP版本号     if (chunk.size() < 3)     {         conn->forceClose();         return;     }      LOG_INFO << "url: " << chunk[1] << " from " << conn->peerAddress().toIpPort();     //inbuf = /register.do?p={%22username%22:%20%2213917043329%22,%20%22nickname%22:%20%22balloon%22,%20%22password%22:%20%22123%22}     std::vector<string> part;     //通过?分割成前后两端,前面是url,后面是参数     StringUtil::Split(chunk[1], part, "?");     //chunk中至少有三个字符串:GET+url+HTTP版本号     if (part.size() < 2)     {         conn->forceClose();         return;     }      string url = part[0];     string param = part[1].substr(2);      if (!Process(conn, url, param))     {         LOG_ERROR << "handle http request error, from:" << conn->peerAddress().toIpPort() << ", request: " << pBuffer->retrieveAllAsString();     }      //短连接,处理完关闭连接     conn->forceClose(); }

但这只能解决发送非法数据的情况,如果一个客户端连上来不给我们发任何数据,这段逻辑就无能为力了。如果不断有客户端这么做,会浪费我们大量的连接资源,所以我们还需要一个定时器去定时检测哪些http连接超过一定时间内没给我们发数据,找到后将连接断开。

上述就是小编为大家分享的怎么实现一个Http服务器了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网服务器频道。

--结束END--

本文标题: 怎么实现一个Http服务器

本文链接: https://www.lsjlt.com/news/239950.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 怎么实现一个Http服务器
    这期内容当中小编将会给大家带来有关怎么实现一个Http服务器,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。说到http协议和http请求,很多人都知道,但是他们真的“知道”吗我面试过很多求职者,一说到ht...
    99+
    2023-06-04
  • Python中怎么实现一个HTTP服务
    Python中怎么实现一个HTTP服务,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。实际上来说,这是一个可以用来共享文件的非常有用的方式。实现一个微型的HTTP...
    99+
    2023-06-17
  • Golang中怎么实现一个HTTP代理服务器
    本篇文章给大家分享的是有关Golang中怎么实现一个HTTP代理服务器,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。定义请求对象,接收客户端请求处理请求,把处理结果返回给客户端...
    99+
    2023-06-03
  • Python探索之实现一个简单的HTTP服务器
    Python标准库中的BaseHTTPServer模块实现了一个基础的HTTP服务器基类和HTTP请求处理类。这在文章python探索之BaseHTTPServer-实现Web服务器介绍中进行了相关的介绍。...
    99+
    2022-06-05
    简单 服务器 Python
  • nodejs中怎么实现一个http请求
    nodejs中怎么实现一个http请求,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。nodejs http请求相关总结通过no...
    99+
    2022-10-19
  • node中怎么实现一个http小爬虫
    这篇文章给大家介绍node中怎么实现一个http小爬虫,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。爬取Node.js 教程首页的所有数据建立node-http.js,其中代码如下,代...
    99+
    2022-10-19
  • 怎么用Node创建一个简单的HTTP服务器
    这篇文章主要介绍“怎么用Node创建一个简单的HTTP服务器”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么用Node创建一个简单的HTTP服务器”文章能帮助大家解决问题。1. 使用Node.js...
    99+
    2023-07-04
  • ASP.NETCore中怎么实现一个Kestrel服务器
    这篇文章给大家介绍ASP.NETCore中怎么实现一个Kestrel服务器,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Kestrel Web服务器的概述Kestrel被认为是较新的ASP.NET应用程序的首选Web服...
    99+
    2023-06-19
  • 使用golang怎么实现一个DNS服务器
    使用golang怎么实现一个DNS服务器?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。golang适合做什么golang可以做服务器端开发,但golang很适合做日志处理、...
    99+
    2023-06-14
  • ASP.NET中怎么实现一个服务器控件
    ASP.NET中怎么实现一个服务器控件,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。ASP.NET组件设计之ASP.NET服务器控件1、用户控件类似page,基本上不需要编程,...
    99+
    2023-06-18
  • Linux中怎么实现一个代理服务器
    本篇文章为大家展示了Linux中怎么实现一个代理服务器,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。  1.安装Linux,不管是在图形,还是文体下都可以,选择最小安装,在安装的时候可以先配置一下外...
    99+
    2023-06-12
  • python一个命令开启http服务器
    1、python开启http服务器 python -m SimpleHTTPServer 8080如果提示错误:python.exe: No modu...
    99+
    2023-01-31
    命令 服务器 python
  • php怎么实现http服务
    本文小编为大家详细介绍“php怎么实现http服务”,内容详细,步骤清晰,细节处理妥当,希望这篇“php怎么实现http服务”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。   ...
    99+
    2022-10-19
  • Qt怎么实现http服务
    本篇内容介绍了“Qt怎么实现http服务”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!先看执行结果:Qt HttpServer左边是开启的Q...
    99+
    2023-07-06
  • 使用Flutter怎么实现一个Http网络请求
    这期内容当中小编将会给大家带来有关使用Flutter怎么实现一个Http网络请求,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1. Http的请求方式简介Http网络请求方式就是描述了客户端想对指定的资源...
    99+
    2023-06-14
  • node.js 核心http模块,起一个服务器,返回一个页面的实例
    如下所示: let http=require("http"); //引入核心http模块 let fs=require("fs"); let mime={ '.js':'application/jav...
    99+
    2022-06-04
    实例 模块 核心
  • 使用node-images怎么实现一个图片服务器
    这篇文章给大家介绍使用node-images怎么实现一个图片服务器,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Edit:2016-5-11 修正了代码里面一些明显的错误,并发布在 a...
    99+
    2022-10-19
  • node中怎么实现一个反向代理服务器
    这期内容当中小编将会给大家带来有关node中怎么实现一个反向代理服务器,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。每当提起反向代理器,人们通常一想到的就是 Nginx,...
    99+
    2022-10-19
  • C语言中怎么实现一个socket.io服务器端
    C语言中怎么实现一个socket.io服务器端,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。在运行socket.io_server之前,需要安装以下依赖:sud...
    99+
    2023-06-17
  • 在java项目中使用 Socket如何实现模拟一个HTTP服务器
    这篇文章给大家介绍在java项目中使用 Socket如何实现模拟一个HTTP服务器,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。HTTP基于TCP协议,协议采用了请求/响应模型。客户端向服务器发送一个请求,请求头包含请...
    99+
    2023-05-31
    java socket http
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作