当前位置: 首页 > news >正文

国家企业信用信息网苏州网站优化

国家企业信用信息网,苏州网站优化,常州营销网站建设,医疗机构 网站备案目录 HTTP协议 预备知识 认识 URL 认识 urlencode 和 urldecode HTTP协议格式 HTTP请求协议格式 HTTP响应协议格式 HTTP的方法 HTTP的状态码 ​编辑HTTP常见Header HTTP实现代码 HttpServer.hpp HttpServer.cpp Socket.hpp log.hpp Makefile Web根目录 H…  目录 HTTP协议 预备知识 认识 URL 认识 urlencode 和 urldecode HTTP协议格式  HTTP请求协议格式 HTTP响应协议格式 HTTP的方法 HTTP的状态码 ​编辑HTTP常见Header HTTP实现代码 HttpServer.hpp HttpServer.cpp Socket.hpp  log.hpp  Makefile  Web根目录 HTTPS协议了解 明文 与 密文 对称加密 与 非对称加密 数据摘要  数据指纹 HTTP协议 预备知识 HTTPHyper Text Transfer Protocol协议又叫做超文本传输协议是一个简单的请求-响应协议HTTP通常运行在TCP之上。 在编写网络通信代码时我们可以自己进行协议的定制但实际有很多优秀的工程师早就已经写出了许多非常成熟的应用层协议其中最典型的就是HTTP协议。  日常生活中为什么不用ip地址和端口呢而是直接使用域名呢 因为域名是很容易被记住,ip地址体验特别差给你个ip地址你都不知道是什么连接 其实我们平时用的域名首先会被解析成IP地址的在进行访问时网络通信真正用到的就是IP地址 我们在浏览器进行访问的时候大部分默认使用的协议是http协议。他们的端口号一般是要固定下来的 其中的网络服务这个端口号都要指明出来不能改一旦改了客户端就找不到你了 认识 URL URLUniform Resource Lacator叫做统一资源定位符也就是我们通常所说的网址是因特网的万维网服务程序上用于指定信息位置的表示方法。 图片 音频...统称为资源所有网络上的资源都可以用唯一的一个字符串标识并且可以获取到我们就称之为统一资源 可是我找到了这台主机和http服务但是我想访问的是什么呢比如我们平时在搜图片的时候这个图片其实是被存储在该主机上的而我们知道Linux一切皆文件所以每个资源在自己的单机上都有自己的所属路径。因此我们还需要有一个带层次的文件路径。来标识我们具体想访问该主机上哪个地方的资源一般来说可能是从web根目录开始 也有可能是相对路径/是文件路径分隔符 我们在上网的时候无非就两种网络行为。1.把别人的东西拿下来比如说下载图片 2.把自己的东西传上去 比如说登录注册 当我们找到了这个资源的路径那么这个路径可能有很多很多格式各种的资源那么我们继续需要锁定其中的一个就需要有查询字符串一般在查询标示符的后面表明该路径下的唯一资源而要锁定唯一资源就需要传入一些指定的参数不同参数之间用去分隔让url支持多参数的提交 而#后面的是片段标示符也就是说可能我们想访问的是该资源的某一个位置可以用这个去进行标识  我们在网络编程中还知道要通过端口号来标识这台主机上的唯一一个服务而该进程自带着网络协议http的解析方法可是我们普通人使用的时候是没有端口号这个概念的 那么浏览器怎么知道你这个端口号呢 因为浏览器会默认使用http协议所以他必须知道绑定443号端口所以默认会在请求里给我们添加端口号 认识 urlencode 和 urldecode urldecode编码 urldecode 函数用于将字符串中的特殊字符转化为 URL 编码格式。在 URL 中某些字符如空格、、、? 等具有特殊意义因此不能直接出现在 URL 中。urldecode 通过将这些字符转换为百分号编码也叫做 URL 编码来避免这些问题。 urldecode解码 urlencode 函数用于将 URL 编码百分号编码的字符串恢复为原始的字符串形式。它将 URL 中的 % 后跟的十六进制数还原回原来的字符。 少量的情况提交或者获取的数据本身包含和url中特殊的字符冲突的字符要求BS双方进行编码(encode)和解码的过程(decode)这样可以保证特殊字符不会在url中出现。浏览器会自动执行 为什么要这样呢 因为它本身就包含这种特殊符号字符的使用用户本身的特殊符号不会造成影响。这个编码和解码并不是为了加密什么的而是为了保证用户提交的数据和url不发生冲突 urldecode转义的规则如下: 将需要转码的字符转为16进制然后从右到左取4位(不足4位直接处理)每2位做一位前面加上%编码成%XY 格式 urldecode就是urlencode的逆过程 HTTP协议格式  介绍 应用层常见的协议有HTTP和HTTPS传输层常见的协议有TCP网络层常见的协议是IP数据链路层对应就是MAC帧了。其中下三层是由操作系统或者驱动帮我们完成的它们主要负责的是通信细节。如果应用层不考虑下三层在应用层自己的心目当中它就可以认为自己是在和对方的应用层在直接进行数据交互。 下三层负责的是通信细节而应用层负责的是如何使用传输过来的数据两台主机在进行通信的时候应用层的数据能够成功交给对端应用层因为网络协议栈的下三层已经负责完成了这样的通信细节而如何使用传输过来的数据就需要我们去定制协议这里最典型的就是HTTP协议。 HTTP是基于请求和响应的应用层服务作为客户端你可以向服务器发起request服务器收到这个request后会对这个request做数据分析得出你想要访问什么资源然后服务器再构建response完成这一次HTTP的请求。这种基于requestresponse这样的工作方式我们称之为cs或bs模式其中c表示clients表示serverb表示browser。 由于HTTP是基于请求和响应的应用层访问因此我们必须要知道HTTP对应的请求格式和响应格式这就是学习HTTP的重点。 HTTP请求协议格式 Request请求分析图 HTTP请求由以下四部分组成  请求行[请求方法][url][http版本]请求报头请求的属性这些属性都是以key: value的形式按行陈列的。空行遇到空行表示请求报头结束。请求正文请求正文允许为空字符串如果请求正文存在则在请求报头中会有一个Content-Length属性来标识请求正文的长度。 其中前面三部分是一般是HTTP协议自带的是由HTTP协议自行设置的而请求正文一般是用户的相关信息或数据如果用户在请求时没有信息要上传给服务器此时请求正文就为空字符串。 你怎么确保正文部分能够一字不落地读完呢 报头属性里面有 标注正文部分的长度的属性所以他只要根据换行规则去读取整个报文然后一直读到空行就说明报头读完了然后再根据 Content-Length (length字段) 直接向后读相应长度的字节 就可以把正文部分也给读完了 HTTP响应协议格式 Response响应示意图 HTTP响应由以下四部分组成 状态行[http版本][状态码][状态码描述] 响应报头响应的属性这些属性都是以key: value的形式按行陈列的。 空行遇到空行表示响应报头结束。 响应正文响应正文允许为空字符串如果响应正文存在则响应报头中会有一个Content-Length属性来标识响应正文的长度。比如服务器返回了一个html页面那么这个html页面的内容就是在响应正文当中的。 使用 Telnet 连接远程主机来模拟客户端进行访问 telnet 主机名 端口号退出 Telnet 会话 Ctrl] telnet quit响应结果解析 fiddle 抓包软件fiddle fiddle为什么能抓包呢 原理:以前是浏览器直接发到网络。fiddle启动之后浏览器发的请求不会直接发到网络中了而是先交给fiddle它进行包装下再发给服务器服务器再把请求响应返回fiddle它再给我们的浏览器 postman 可以通过它发送具体请求 在Linux系统中recv 函数用于接收通过套接字传输的数据。它是一个阻塞函数通常用于从网络连接中接收数据。recv 函数是POSIX标准的一部分可以用来从连接的套接字如TCP套接字中接收数据。 ssize_t recv(int sockfd, void *buf, size_t len, int flags);参数说明 //sockfd要接收数据的套接字描述符。它通常是由 socket()、accept() 或 connect() 等函数创建的套接字。 //buf接收数据的缓冲区。 //len缓冲区的大小指定最多接收多少字节。 //flags接收数据的选项可以是以下之一或它们的组合 //MSG_OOB接收带外数据。 //MSG_PEEK从队列中查看数据但不移除它们。 //MSG_WAITALL等待接收到指定大小的所有数据。返回值 //如果成功recv 返回接收到的字节数即读取的数据量。如果接收到的数据量小于指定的 len可能是因为连接中没有更多的数据。 //返回值为0表示对方关闭了连接TCP连接的正常关闭。 //返回值为-1表示出错可以通过 errno 获取错误码。 测试效果  我们的网页难道每一次都要通过静态编码写到服务器里面吗我们的网页应该单独的是一个独立的文件。需要访问哪个就通过http访问就行了 即http响应的正文部分要放在网页(index.html)里面。让内容不要静态编码而是动态的去让服务器响应给浏览器 对index文件修改之后保存就能动态刷新内容了不需要重启服务器  我们的文件可能不止一个包括图片 样式文件 js文件 各种各样的文件在Linux存在的情况下一定会存在个目录结构这个目录结构肯定整体能被web访问 一个Http协议一定有自己的web根目录这个目录也可以由自己去指定。但是这样无论我们怎么请求只能返回一个同一个文件等未来你可能需要不同的文件该怎么办呢 很明显将来你不可能把所有东西都放在这个文件下这样是不现实的。所以我们现在要实现由客户端告诉我它想要什么文件我就给它哪个文件那该怎么实现呢 实现的思路就是我们要提取发来的请求。我们不难发现Request请求行中的第一行URL就包括了用户想请求哪个目录 代码实现思路 把指明的路径拼上前缀此时就能够动态的在服务器上找到在wwwroot目录下指定的文件了。当未来加入图片等内容的时候就可以在wwwroot目录下用树状结构组织好去或许对应的内容了 我们可以定义个config配置文件把web根目录放在config里面。当服务器启动的时候就读取配置文件里面所需的内容了 如何拿到URL 把发来的请求进行反序列化拿到请求行和请求报头(正文内容还没添加) 在请求行中拿到URL 我们可以发现HTTP协议处理本质上就是文本的处理跟我们之前讲的自定义协议是一样的 我们可以用一个东西stringstream 进行分割字符串 用法如下 std::stringstream 在 C 中可以用于分割字符串其基本原理是通过将字符串加载到流中然后利用流操作符 () 按照空格或其他分隔符逐步提取数据。这个过程利用了流的特性流会根据输入格式将字符串转换为不同的数据类型。 分割字符串的原理 std::stringstream 分割字符串的过程可以简单理解为 加载字符串将需要分割的字符串传递给 std::stringstream 对象这样就可以使用流操作符读取字符串中的各个部分。使用流提取符提取数据流操作符 () 会逐个提取数据并且在遇到空格或其他分隔符时停止提取。重复提取直到流为空你可以反复使用流操作符来从流中提取多个数据直到流的内容全部被提取完。 #includesstream using namespace std;int main() {stringstream ss(abc def ghi);string s1, s2, s3;ss s1 s2 s3;cout s1 endl;cout s2 endl;cout s3 endl;return 0; }提取URL 如果在访问的时候不指定目录比如说我们要访问(www.baidu.com那就会默认去访问www.baidu.com/)那就是默认会带一个/代表着根目录只会把网站的首页给你。也就是index.html。而不会把根目录下的所有内容给你。 那么我们也要实现这个思路当你默认访问的时候设置把首页给你当你指定访问的时候就去访问对应的目录 const std::string homepage index.html;//解析之后做判断 file_path wwwroot; // ./wwwroot if(url / || url /index.html) {file_path /;file_path homepage; // ./wwwroot/index.html } else file_path url; // /a/b/c/d.html-./wwwroot/a/b/c/d.html 可以看到当我输入的url是/a/b/c.html服务器自动把路径给我填充为 ./wwwroot/a/b/c/d.html 当我访问的是默认的话服务器也会自动填充为首页目录 下一步就是我们新建别的目录 测试结果: 在下一步可是对我们来说我们也不这样访问呀如果我们访问京东的时候一般都是上一页下一页从一个链接跳转到另一个链接的去访问。这个时候我们就需要把URL给维护起来了 此时就需要这个代码了 a hrefxxxx到第二张网页/a 让 里面的内容具有超链接的作用。同样的也能跳转到自己的地址到第三张网页...  重点是理解HTTP的请求无非就是通过套接字读到了HTTP的请求然后按照格式给解析出来提取URL给他响应再把响应返回去。HTTP底层就是TCP HTTP的方法 同学们您们的请求全部都是get方法那么什么叫做GET方法呢 但是一般来说最常用的就是 GET 和 POST  GET方法一般用于获取某种资源信息而POST方法一般用于将数据上传给服务器。但实际我们上传数据时也有可能使用GET方法比如百度提交数据时实际使用的就是GET方法。 我们在日常使用某些网站的时候是如何把数据提交给服务器的呢日常又是怎么提交的呢 其实数据都是通过表单提供的 GET 方法的提交方式 POST 方法的提交方式 GET方法和POST方法都可以带参 GET方法是通过url传参的。POST方法是通过正文传参的。 从GET方法和POST方法的传参形式可以看出POST方法能传递更多的参数因为url的长度是有限制的POST方法通过正文传参就可以携带更多的数据。 此外使用POST方法传参更加私密因为POST方法不会将你的参数回显到url当中此时也就不会被别人轻易看到。不能说POST方法比GET方法更安全因为POST方法和GET方法实际都不安全要做到安全只能通过加密来完成。 其实提交参数的本质意义就是fork子进程让他通过进程替换去执行这个程序然后我们再通过进程间通信的方法将参数传递给他然后让他完成相应的功能 HTTP的状态码 最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway) 404表示资源不存在比如客户端请求的资源服务器打开失败所以产生错误是客户端的错误所以一般来说我们除了要写一个网站首页的文件还需要写一个如果请求失败的一个返回404页面的网页 模拟下404界面的实现 当收到的text为空时说明没有找到路径那就直接去找err.html提示404 Not Found 403是禁止访问一般就是因为你没有授权然后去访问了服务端不让你访问的信息  504服务器错误一般来说就是比如连接不上或者是服务的线程创建失败 3XX一般是由于我要访问的服务器可能处于某种原因无法为我提供服务所以他会传一个地址告诉客户端你想要的资源应该去这里查找通过参数Location这就是重定向 Redirection详解 重定向分为永久重定向和临时重定向  讲个故事        比如说你的学校东门有一家火锅店非常有名但是他需要长时间的装修为了继续生意他将生意开到了学校的西门然后在东门这里贴了一张纸条“正在装修请到西门用餐”这个时候你和你的同学到东门时看到了这个纸条于是就到西门去吃了了过了一礼拜你们还打算去的时候你知道东门那边只是暂时关闭所以你还是会先去东门看看如果还没开的话才会去西门这个其实就是 临时重定向因为我知道这个店只是暂时开到西门随时可能会回来       而后来东门开了的时候为了能够让之前去西门吃饭的同学知道东门开启了他会在之前西门的地方贴上“东门已经装修完成以后请都前往东门用餐” 这个时候你们就知道这个店会一直在东门这其实就是 永久重定向 如果某个网站是永久重定向那么第一次访问该网站时由浏览器帮你进行重定向但后续再访问该网站时就不需要浏览器再进行重定向了此时你访问的直接就是重定向后的网站。而如果某个网站是临时重定向那么每次访问该网站时如果需要进行重定向都需要浏览器来帮我们完成重定向跳转到目标网站。 所以有的网站时间比较老做更新的时候需要把域名和网址换了可是很多老用户并不知道所以就会给老网站部署永久性定向服务让用户直接跳转到新网站 //构建HTTP响应 string status_line http/1.1 307 Temporary Redirect\n; //状态行 string response_header Location: https://www.csdn.net/\n; //响应报头 string blank \n; //空行 string response status_line response_header blank; //响应报文 //响应HTTP请求 send(sock, response.c_str(), response.size(), 0);当我们用telnet命令登录我们的服务器时向服务器发起HTTP请求时此时服务器给我们的响应就是状态码307响应报头当中是Location字段对应的就是CSDN首页的网址  telnet命令实际上只是一来一回如果我们用浏览器访问我们的服务器当浏览器收到这个HTTP响应后还会对这个HTTP响应进行分析当浏览器识别到状态码是307后就会提取出Location后面的网址然后继续自动对该网站继续发起请求此时就完成了页面跳转这样的功能。 此时当浏览器访问我们的服务器时就会立马跳转到CSDN的首页。 HTTP常见Header 以下是常见的Header Content-Type: 数据类型(text/html等) Content-Length: Body的长度Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上; User-Agent: 声明用户的操作系统和浏览器版本信息; referer: 当前页面是从哪个页面跳转过来的; location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问; Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;connection是否支持长连接是基于服务费和客户端的版本去协商的 Content-Type 读html的时候他是文本文件那么就可以按照字符串读但是如果我们是图片资源png那么就得按照二进制文件的方式来读因此在读取html之前需要确认一下文件格式而不同的格式对应的content-type可以到网上搜索对照表。所以读网页之前必须确定一下后缀然后不同的后缀就根据content-type对照表去找。 常见的Content-Type Host  Host字段表明了客户端要访问的服务的IP和端口比如当浏览器访问我们的服务器时浏览器发来的HTTP请求当中的Host字段填的就是我们的IP和端口。但客户端不就是要访问服务器吗为什么客户端还要告诉服务器它要访问的服务对应的IP和端口 因为有些服务器实际提供的是一种代理服务也就是代替客户端向其他服务器发起请求然后将请求得到的结果再返回给客户端。在这种情况下客户端就必须告诉代理服务器它要访问的服务对应的IP和端口此时Host提供的信息就有效了。 connection 短连接其实就是一次请求响应一个资源然后就关闭连接 长连接其实就是一次请求连接上之后可以一直服务直到服务结束再关闭连接 为什么要有长连接呢 因为一个巨大的网页上的元素是很多的而每一个元素其实就是一个资源所以我们在发出http请求申请到网页资源的时候同时也需要把网页上附带的资源比如图片、音频) 都申请了。比如服务器要获取100个图片的网页要发起101次HTTP的请求对我们的服务器来说要一次性创建几十个线程而HTTP底层是基于TCP的TCP是面向连接的是短连接所以如果用短连接的话显然效率是不够高的 长连接和短连接并没有绝对的优劣只不过应用场景不一样 但是要注意的是具体采用长连接还是短连接是要基于双方的HTTP版本协商的其中HTTP1.0其实就是短连接  而HTTP1.1其实就是长连接   要支持长连接的话必须要求双方的版本都是1.1   这样Connection就会呈现keep-alive表示支持长连接  Referer Referer代表的是你当前是从哪一个页面跳转过来的。Referer记录上一个页面的好处一方面是方便回退另一方面可以知道我们当前页面与上一个页面之间的相关性。 Cookie HTTP实际上是一种无状态协议HTTP的每次请求/响应之间是没有任何关系的但你在使用浏览器的时候发现并不是这样的。 比如当你登录一次CSDN后就算你把CSDN网站关了甚至是重启电脑当你再次打开CSDN网站时CSDN并没有要求你再次输入账号和密码这实际上是通过cookie技术实现的点击浏览器当中锁的标志就可以看到对应网站的各种cookie数据。 这些cookie数据实际都是对应的服务器方写的如果你将对应的某些cookie删除那么此时可能就需要你重新进行登录认证了因为你删除的可能正好就是你登录时所设置的cookie信息。 Cookie是什么 因为HTTP是一种无状态协议如果没有cookie的存在那么每当我们要进行页面请求时都需要重新输入账号和密码进行认证这样太麻烦了。  首先我们要知道HTTP默认是无状态的而他之所以能够知道你你处在登录状态是因为你之前登录的时候在浏览器里形成了一个cookie文件这个文件里存储着你在这个网站的认证信息而当你打开这个网站时浏览器向对应服务端发送请求的时候会将你的认证信息就是用户名和密码放在cookie参数里面带过去直接认证认证其实就是拿着你的用户名和密码去他后端的数据库做搜索所以你想使用的前提是必须得注册才能在他的数据库里留存数据认证通过之后会直接将你从原先的登录页面重定向到目标页面这样你就不需要再次登录了当然这个保存一般是有时间限制的 从第一次登录认证之后浏览器再向该网站发起的HTTP请求当中就会自动包含一个cookie字段其中携带的就是我第一次的认证信息此后对端服务器需要对你进行认证时就会直接提取出HTTP请求当中的cookie字段而不会重新让你输入账号和密码了。也就是在第一次认证登录后后续所有的认证都变成了自动认证这就叫做cookie技术。也叫做HTTP会话保持功能 内存级别文件级别 cookie就是在浏览器当中的一个小文件文件里记录的就是用户的私有信息。cookie文件可以分为两种一种是内存级别的cookie文件另一种是文件级别的cookie文件。 将浏览器关掉后再打开访问之前登录过的网站如果需要你重新输入账号和密码说明你之前登录时浏览器当中保存的cookie信息是内存级别的。将浏览器关掉甚至将电脑重启再打开访问之前登录过的网站如果不需要你重新输入账户和密码说明你之前登录时浏览器当中保存的cookie信息是文件级别的。 cookie被盗  如果你浏览器当中保存的cookie信息被非法用户盗取了那么此时这个非法用户就可以用你的cookie信息以你的身份去访问你曾经访问过的网站我们将这种现象称为cookie被盗取了。 比如你不小心点了某个链接这个链接可能就是一个下载程序当你点击之后它就会通过某种方式把程序下载到你本地并且自动执行该程序该程序会扫描你的浏览器当中的cookie目录把所有的cookie信息通过网络的方式传送给恶意方当恶意方拿到你的cookie信息后就可以拷贝到它的浏览器对应的cookie目录当中然后以你的身份访问你曾经访问过的网站。 所以我们会面临两个问题1cookie被盗取 2个人信息泄露 我们要知道我们小白用户的防范能力基本为0他们的电脑在黑客的眼里其实就相当于裸奔比如你不小心点开了一个病毒你或许可以通过杀毒软件去杀毒但是也很有可能在你清理这个病毒之前你的信息就已经被窃取了 所以显然不能让客户端来维护这个安全问题必须由更专业的服务端来维护 SessionID 所以引入了session技术我们输入用户名密码的时候他会将这个用户名密码存在服务端然后生成一个session id数字指纹返回给客户端存在cookie文件里然后服务端会将session id管理起来redis这样认证的时候就会用cookie文件里面存储的session id去服务端做对比只要通过了就可以直接登录了 当我们第一次登录某个网站输入账号和密码后服务器认证成功后还会服务端生成一个对应的SessionID这个SessionID与用户信息是不相关的。系统会将所有登录用户的SessionID值统一维护起来。 此时当认证通过后服务端在对浏览器进行HTTP响应时就会将这个生成的SessionID值响应给浏览器。浏览器收到响应后会自动提取出SessionID的值将其保存在浏览器的cookie文件当中。后续访问该服务器时对应的HTTP请求当中就会自动携带上这个SessionID。而服务器识别到HTTP请求当中包含了SessionID就会提取出这个SessionID然后再到对应的集合当中进行对比对比成功就说明这个用户是曾经登录过的此时也就自动就认证成功了然后就会正常处理你发来的请求这就是我们当前主流的工作方式。使用SessionID本质上是防止了个人信息泄露了。因为你的个人信息已经是在服务器上维护了而不是在客户端上维护了 安全是相对的 引入SessionID之后浏览器当中的cookie文件保存的是SessionID此时这个cookie文件同样可能被盗取。此时用户的账号和密码虽然不会泄漏了但用户对应的SessionID是会泄漏的非法用户仍然可以盗取我的SessionID去访问我曾经访问过的服务器相当于还是存在刚才的问题。 之前的工作方式就相当于把账号和密码信息在浏览器当中再保存一份每次请求时都自动将账号和密码的信息携带上但是账号和密码一直在网当中发送太不安全了。因此现在的工作方式是服务器只有在第一次认证的时候需要在网络中传输账号和密码此后在网络上发送的都是SessionID。 这种方法虽然没有真正解决安全问题但这种方法是相对安全的。但是跟之前最大的区别就是session id是由服务端统一管理的也就意味着他具备回收、停用、甄别异常等能力比如说他发现你的ip地址突然发生了很大的变动察觉异常就会暂时给你停止等你发现问题了再次申诉的时候他就会把你之前的session id清理掉然后重新分配 这其实就是达到了控制客户端的目的  不过在安全领域有一个准则如果破解某个信息的成本已经远远大于破解之后获得的收益说明做这个事是赔本的那么就可以说这个信息是安全的。 HTTP实现代码 HttpServer.hpp #pragma once #include iostream #include string #include pthread.h #include fstream #include vector #include sstream #include sys/types.h #include sys/socket.h #include unordered_map #include Socket.hpp #include Log.hppconst std::string wwwroot./wwwroot; // web 根目录 const std::string sep \r\n; const std::string homepage index.html;static const int defaultport 8082;class HttpServer;class ThreadData { public:ThreadData(int fd, HttpServer *s) : sockfd(fd), svr(s){}public:int sockfd;HttpServer *svr; };class HttpRequest { public:void Deserialize(std::string req){while(true){std::size_t pos req.find(sep);if(pos std::string::npos) break;std::string temp req.substr(0, pos);if(temp.empty()) break;req_header.push_back(temp);req.erase(0, possep.size());}text req;}// .png:image/pngvoid Parse(){std::stringstream ss(req_header[0]);ss method url http_version;file_path wwwroot; // ./wwwrootif(url / || url /index.html) {file_path /;file_path homepage; // ./wwwroot/index.html}else file_path url; // /a/b/c/d.html-./wwwroot/a/b/c/d.htmlauto pos file_path.rfind(.);if(pos std::string::npos) suffix .html;else suffix file_path.substr(pos);}void DebugPrint(){for(auto line : req_header){std::cout -------------------------------- std::endl;std::cout line \n\n;}std::cout method: method std::endl;std::cout url: url std::endl;std::cout http_version: http_version std::endl;std::cout file_path: file_path std::endl;std::cout text std::endl;} public:std::vectorstd::string req_header;std::string text;// 解析之后的结果std::string method;std::string url;std::string http_version;std::string file_path; // ./wwwroot/a/b/c.html 2.pngstd::string suffix; };class HttpServer { public:HttpServer(uint16_t port defaultport) : _port(port){content_type.insert({.html, text/html});content_type.insert({.png, image/png});content_type.insert({.jpg, image/jpeg});}bool Start(){_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();for (;;){std::string clientip;uint16_t clientport;int sockfd _listensock.Accept(clientip, clientport);if (sockfd 0)continue;lg(Info, get a new connect, sockfd: %d, sockfd);pthread_t tid;ThreadData *td new ThreadData(sockfd, this);pthread_create(tid, nullptr, ThreadRun, td);}}static std::string ReadHtmlContent(const std::string htmlpath){// 坑std::ifstream in(htmlpath, std::ios::binary);if(!in.is_open()) return ;in.seekg(0, std::ios_base::end);auto len in.tellg();in.seekg(0, std::ios_base::beg);std::string content;content.resize(len);in.read((char*)content.c_str(), content.size());//std::string content;//std::string line;//while(std::getline(in, line))//{// content line;//}in.close();return content;}std::string SuffixToDesc(const std::string suffix){auto iter content_type.find(suffix);if(iter content_type.end()) return content_type[.html];else return content_type[suffix];}void HandlerHttp(int sockfd){char buffer[10240];ssize_t n recv(sockfd, buffer, sizeof(buffer) - 1, 0); // bugif (n 0){buffer[n] 0;std::cout buffer std::endl; // 假设我们读取到的就是一个完整的独立的http 请求HttpRequest req;req.Deserialize(buffer);req.Parse();req.DebugPrint();//std::string path wwwroot;//path url; // wwwroot/a/a/b/index.html// 返回响应的过程std::string text;bool ok true;text ReadHtmlContent(req.file_path); // 失败if(text.empty()){ok false;std::string err_html wwwroot;err_html /;err_html err.html;text ReadHtmlContent(err_html);}std::string response_line;if(ok)response_line HTTP/1.0 200 OK\r\n;elseresponse_line HTTP/1.0 404 Not Found\r\n;//response_line HTTP/1.0 302 Found\r\n;std::string response_header Content-Length: ;response_header std::to_string(text.size()); // Content-Length: 11response_header \r\n;response_header Content-Type: ;response_header SuffixToDesc(req.suffix);response_header \r\n;response_header Set-Cookie: namehahapasswd12345;response_header \r\n;//response_header Location: https://www.qq.com\r\n;std::string blank_line \r\n; // \nstd::string response response_line;response response_header;response blank_line;response text;send(sockfd, response.c_str(), response.size(), 0);}close(sockfd);}static void *ThreadRun(void *args){pthread_detach(pthread_self());ThreadData *td static_castThreadData *(args);td-svr-HandlerHttp(td-sockfd);delete td;return nullptr;}~HttpServer(){}private:Sock _listensock;uint16_t _port;std::unordered_mapstd::string, std::string content_type; }; HttpServer.cpp #include HttpServer.hpp #include iostream #include memory #include pthread.h #include Log.hppusing namespace std;int main(int argc, char *argv[]) {if(argc ! 2){exit(1);}uint16_t port std::stoi(argv[1]);// HttpServer *svr new HttpServer();// std::uniqueHttpServer svr(new HttpServer());std::unique_ptrHttpServer svr(new HttpServer(port));svr-Start();return 0; } Socket.hpp  #pragma once#include iostream #include string #include unistd.h #include cstring #include sys/types.h #include sys/stat.h #include sys/socket.h #include arpa/inet.h #include netinet/in.h #include Log.hppenum {SocketErr 2,BindErr,ListenErr, };// TODO const int backlog 10;class Sock { public:Sock(){}~Sock(){}public:void Socket(){sockfd_ socket(AF_INET, SOCK_STREAM, 0);if (sockfd_ 0){lg(Fatal, socker error, %s: %d, strerror(errno), errno);exit(SocketErr);}int opt 1;setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt));}void Bind(uint16_t port){struct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;local.sin_port htons(port);local.sin_addr.s_addr INADDR_ANY;if (bind(sockfd_, (struct sockaddr *)local, sizeof(local)) 0){lg(Fatal, bind error, %s: %d, strerror(errno), errno);exit(BindErr);}}void Listen(){if (listen(sockfd_, backlog) 0){lg(Fatal, listen error, %s: %d, strerror(errno), errno);exit(ListenErr);}}int Accept(std::string *clientip, uint16_t *clientport){struct sockaddr_in peer;socklen_t len sizeof(peer);int newfd accept(sockfd_, (struct sockaddr*)peer, len);if(newfd 0){lg(Warning, accept error, %s: %d, strerror(errno), errno);return -1;}char ipstr[64];inet_ntop(AF_INET, peer.sin_addr, ipstr, sizeof(ipstr));*clientip ipstr;*clientport ntohs(peer.sin_port);return newfd;}bool Connect(const std::string ip, const uint16_t port){struct sockaddr_in peer;memset(peer, 0, sizeof(peer));peer.sin_family AF_INET;peer.sin_port htons(port);inet_pton(AF_INET, ip.c_str(), (peer.sin_addr));int n connect(sockfd_, (struct sockaddr*)peer, sizeof(peer));if(n -1) {std::cerr connect to ip : port error std::endl;return false;}return true;}void Close(){close(sockfd_);}int Fd(){return sockfd_;}private:int sockfd_; }; log.hpp  #pragma once#include iostream #include time.h #include stdarg.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #include stdlib.h#define SIZE 1024#define Info 0 #define Debug 1 #define Warning 2 #define Error 3 #define Fatal 4#define Screen 1 #define Onefile 2 #define Classfile 3#define LogFile log.txtclass Log { public:Log(){printMethod Screen;path ./log/;}void Enable(int method){printMethod method;}std::string levelToString(int level){switch (level){case Info:return Info;case Debug:return Debug;case Warning:return Warning;case Error:return Error;case Fatal:return Fatal;default:return None;}}// void logmessage(int level, const char *format, ...)// {// time_t t time(nullptr);// struct tm *ctime localtime(t);// char leftbuffer[SIZE];// snprintf(leftbuffer, sizeof(leftbuffer), [%s][%d-%d-%d %d:%d:%d], levelToString(level).c_str(),// ctime-tm_year 1900, ctime-tm_mon 1, ctime-tm_mday,// ctime-tm_hour, ctime-tm_min, ctime-tm_sec);// // va_list s;// // va_start(s, format);// char rightbuffer[SIZE];// vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);// // va_end(s);// // 格式默认部分自定义部分// char logtxt[SIZE * 2];// snprintf(logtxt, sizeof(logtxt), %s %s\n, leftbuffer, rightbuffer);// // printf(%s, logtxt); // 暂时打印// printLog(level, logtxt);// }void printLog(int level, const std::string logtxt){switch (printMethod){case Screen:std::cout logtxt std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string logname, const std::string logtxt){std::string _logname path logname;int fd open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // log.txtif (fd 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string logtxt){std::string filename LogFile;filename .;filename levelToString(level); // log.txt.Debug/Warning/FatalprintOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t time(nullptr);struct tm *ctime localtime(t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), [%s][%d-%d-%d %d:%d:%d], levelToString(level).c_str(),ctime-tm_year 1900, ctime-tm_mon 1, ctime-tm_mday,ctime-tm_hour, ctime-tm_min, ctime-tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式默认部分自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), %s %s, leftbuffer, rightbuffer);// printf(%s, logtxt); // 暂时打印printLog(level, logtxt);} private:int printMethod;std::string path; };Log lg;// int sum(int n, ...) // { // va_list s; // char* // va_start(s, n);// int sum 0; // while(n) // { // sum va_arg(s, int); // printf(hello %d, hello %s, hello %c, hello %d,, 1, hello, c, 123); // n--; // }// va_end(s); //s NULL // return sum; // } Makefile  HttpServer:HttpServer.ccg -o $ $^ -stdc11 -lpthread .PHONY:clean clean:rm -f HttpServer Web根目录 HTTPS协议了解 早期很多公司刚起步的时候使用的应用层协议都是HTTP而HTTP无论是用GET方法还是POST方法传参都是没有经过任何加密的因此早期很多的信息都是可以通过抓包工具抓到的。 为了解决这个问题于是出现了HTTPS协议HTTPS实际就是在应用层中加了一层加密层SSLTLS这层加密层本身也是属于应用层的它会对用户的个人信息进行各种程度的加密。HTTPS在交付数据时先把数据交给加密层由加密层对数据加密后再交给传输层。 当然通信双方使用的应用层协议必须是一样的因此对端的应用层也必须使用HTTPS当对端的传输层收到数据后会先将数据交给加密层由加密层对数据进行解密后再将数据交给应用层。 此时数据只有在用户层应用层是没有被加密的而在应用层往下以及网络当中都是加密的这就叫做HTTPS。 明文 与 密文 明文 (Plaintext)指的是未经加密的原始数据或信息。它是人类可以直接读取的内容比如普通的文本文件、电子邮件内容、文件内容等。例如“Hello, how are you?” 这个句子就是明文。 密文 (Ciphertext)指的是通过加密算法将明文转化后的形式目的是保护数据的隐私。密文通常是无法直接理解的只有通过解密操作才能还原成明文。例如使用加密算法加密后原来的文本可能变成类似“Xy2#9kfj23”这样的内容。 对称加密 与 非对称加密 加密的方式可以分为对称加密和非对称加密 采用单钥密码系统的加密方法同一个密钥可以同时用作信息的加密和解密这种加密方法称为对称加密。采用公钥和私钥来进行加密和解密用其中一个密钥进行加密就必须用另一个密钥进行解密这种加密方法称为非对称加密。 对称加密  采用单钥密码系统的加密方法同一个密钥可以同时用作信息的加密和解密这种加密方法称为对称加密也称为单密钥加密特征加密和解密所用的密钥是相同的。 常见对称加密算法(了解)DES、3DES、AES、TDEA、Blowfish、RC2 等 特点算法公开、计算量⼩、加密速度快、加密效率⾼ 对称加密其实就是通过同一个 密钥 , 把明文加密成密文, 并且也能把密文解密成明文。 一个简单的对称加密, 按位异或 假设 明文 a 1234, 密钥 key 8888则加密 a ^ key 得到的密文 b 为 9834。然后针对密文 9834 再次进行运算 b ^ key, 得到的就是原来的明文 1234. (对于字符串的对称加密也是同理, 每一个字符都可以表⽰成一个数字) 非对称加密  需要两个密钥来进行加密和解密这两个密钥是公开密钥public key简称公钥和私有密钥private key简称私钥。 常见非对称加密算法(了解)RSADSAECDSA 特点算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂而使得加密解密速度没有对称加密解密的速度快 • 通过公钥对明文加密, 变成密文 • 通过私钥对密文解密, 变成明文 或 • 通过私钥对明文加密, 变成密文 • 通过公钥对密文解密, 变成明文 非对称加密的数学原理比较复杂, 涉及到一些 数论 相关的知识. 这里举一个简单的生活上的例子。 A 要给 B 一些重要的文件, 但是 B 可能不在。 于是 A 和 B 提前做出约定: B 说: 我桌子上有个盒子, 然后我给你一把锁, 你把文件放盒子里用锁锁上, 然后我回头拿着钥匙来开锁取文件。 在这个场景中, 这把锁就相当于公钥, 钥匙就是私钥。 公钥给谁都行(不怕泄露), 但是 私钥只有 B 自己持有。持有私钥的人才能解密。 所以要注意的是公钥不怕泄露私钥不能泄露。 数据摘要  数据指纹 数据摘要  数据指纹 数据摘要又被称为数据指纹其基本原理是利用单向散列函数Hash函数对数据进行运算生成一串固定长度的数字摘要散列值。 摘要的特征数字摘要并不是一种加密机制数据摘要具有不可逆性即无法从摘要推导出原始数据。 摘要的应用数据摘要用于验证数据的完整性确保数据在传输或存储过程中没有被修改。 摘要的常见算法有MD5、SHA1、SHA256、SHA512等。 数据签名 数据签名是在数据摘要的基础上添加了非对称的加密操作用于验证数据的完整性和真实性。 数据签名包含了数据摘要、公钥密码学算法和数字证书等技术。发送者使用私钥对摘要进行加密形成签名接收者使用发送者的公钥对签名进行解密和验证。 数据签名不仅验证数据的完整性还验证发送者的身份确保数据的真实性和不可否认性。
http://www.w-s-a.com/news/156481/

相关文章:

  • 长寿做网站的电话怎么快速刷排名
  • 上海市中学生典型事例网站邯郸全网推广
  • 厦门网站建设680元好男人的最好的影院
  • 石家庄网站建设设计产品设计专业就业前景
  • 网站移动排名做最好最全的命理网站
  • 网站怎么防黑客杭州市做外贸网站的公司
  • 网站推广公司认准乐云seo易语言做网站登录
  • 配色设计网站推荐网站下拉菜单重叠
  • 内容展示型网站特点在北京注册公司需要多少钱
  • h5网站源代码创意设计理念
  • 岳阳网站开发服务推广运营平台
  • 网站开发得多长时间湖南建设人力资源网证书查询
  • 论坛网站开发网络营销是什么时候产生的
  • 帮人做网站赚钱无忧软文网
  • 做网站要不要营业执照重庆网站优化seo公司
  • 学院宣传网站建设简介做网站没灵感
  • 网站建设终稿确认书网站意义学校
  • 3小时网站建设平台专业制作教学课件
  • 曲阜网站建设百度开户现货黄金什么网站可以做直播
  • 比较好的企业建站平台小程序开发外包该注意些什么
  • 建行官网官网网站吗二次元风格wordpress模板
  • 怎样开通自己的网站网址导航哪个主页最好
  • 大良o2o网站建设详情页设计说明怎么写
  • 您与此网站之间建立的连接不安全汽车cms系统是什么意思
  • 有没有做logo的网站企业网站的内容营销
  • 哈尔滨做企业网站怎么做网站自动响应
  • 网站建设硬件和软件技术环境配置签约做网站模板
  • 教育网站建设的素材手机app制作流程
  • 免费行情软件网站大全下载网站备案查询
  • flex网站模板wordpress实时预览