北京微网站建设设计服务商开发公司交房归物业公司交给物业公司

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

北京微网站建设设计服务商,开发公司交房归物业公司交给物业公司,三木做网站,北仑做网站文章目录url统一资源定位符http协议介绍GET vs POSThttp状态码http常见headercookie session上篇博客定制了一个协议#xff0c;该协议用来进行简单的计算#xff0c;其包含了数据的序列化和反序列化#xff0c;编码和解码的定制#xff0c;并且该协议基于TCP通信#xf… 文章目录url统一资源定位符http协议介绍GET vs POSThttp状态码http常见headercookie session上篇博客定制了一个协议该协议用来进行简单的计算其包含了数据的序列化和反序列化编码和解码的定制并且该协议基于TCP通信是一种客户端请求服务端响应的模型。如果你也实现了一个自己的协议肯定会有疑问自己定制的协议不够成熟而且应用场景有限有没有一个成熟且应用场景广泛的协议这还真有它就是上网必须使用的协议——httphttps url统一资源定位符 要了解http就要先了解url 协议名表示该url采用的协议 登录信息一般在url中被隐去其体现在http请求的报头或者正文当中 服务器地址也称域名域名最终会被解析为IP地址 服务器端口号网络通信的本质是进程间通信所以需要告知IP端口号使通信能够进行。但是由于服务器的端口号通常是固定的不写端口号时端口号也能通过其他特殊方法得知通信照样能进行 文件路径用具体的路径来表示想要访问服务器的哪些资源 查询字符串通常用来过滤网页中的信息得到想要的信息 片段标识符也称锚点用来指定网页的停靠位置或者音视频的播放位置 可以看到url中不同字段需要用特殊符号进行分隔这就意味着每一字段中不能出现这些特殊符号或者说如果出现了这些特殊符号需要对这些特殊符号做处理。比如搜索c这这个关键字 由于‘‘是特殊字符所以其被编码成其他格式。再者搜索汉字时汉字也会被重新编码 虽然url栏显式的是汉字但是把url复制下来再粘贴得到的url是 https://www.baidu.com/s?wd%E5%93%88%E5%93%88 这里说明一下特殊字符的编码规则将特殊字符转换成十六进制以两个十六进制数为一组从低到高一次取出每一组再它们的前面加上%编码成%XY的形式。查询ASCII码表的码值为43表示成十六进制是2B所以在url中其被编码成%2B 由于url采用的是utf-8编码格式在该格式下汉字被编码成3个字节由于两个十六进制数表示1个字节刚才“哈哈”被编码后有6组%XY格式的数据也就是6个字节。关于把特殊字符进行编码的过程我们叫做urlencode将%XY格式的数据解码的过程我们叫做urldecode 再回过头来看url其全称是Uniform Resource Locator统一资源定位符通过url我们就能定位互联网上某一台主机的某些资源。我们使用http协议进行请求就是请求获取某一天主机服务器上的某些资源音视频文本当然服务器的响应就是将客户端请求获取的资源返回。所以说我们访问的网页看到的视频文字不是凭空产生的这些资源都是存储在某些服务器的磁盘上在我们请求资源时由服务器发送给我们的。 http协议介绍 比起上篇博客自己定制的协议http协议能够使用的场景实在是太多了同样的关于http的协议格式也是更复杂的 可以看到http协议格式使用\r\n作为不同字段的分隔符以http请求为例在第一个分隔符之前的数据就是http的请求行里面含有 method具体http请求的方法如GETPOST url刚才说过的url用来定位具体资源 http/1.1http协议的版本 在第一行请求行之后的字段就是请求报头其中可能含有客户端主机的信息连接的属性有效载荷的长度等等信息这些信息以key:value的格式保存在报头中由于这些字段以\r\n分隔所以可以直观的认为报头的每一行都是一对key:value信息。继续这样进行读取我们会遇到一个空行也就是说在报头的最后一个key:value后会有两个\r\n前一个\r\n用来分隔最后的key:value字段那么后一个key:value是用来划分有效载荷与报头的读取http请求时如果遇到了一个空行就说明接下来的数据是这次请求的有效载荷了。客户端的请求无非两种一是请求获取服务端的资源二是请求将客户端的资源上传到服务端这些信息都将在有效载荷中体现 至于服务端的响应其格式与客户端的请求几乎相同都是三个字段响应行响应报头有效载荷。它们的分隔规则是一样的不同的是响应行中的数据 http/1.1 状态码 状态码描述 可以看到请求和响应都含有协议的具体版本信息这样做的目的是为了使通信能够正常进行通信双方要保证通信协议版本的一致性所以客户端和服务端互相发送http协议的版本如果两者的版本不同将按照以较低版本进行通信的原则进行协议的调整。响应行还包括了状态码和其描述通常我们请求的网页都是能正常访问的但是也会有错误情况出现错误码就表征了请求中的错误最常见到的错误码就是404如果你要访问的服务端资源不存在那么服务端的状态码将被设置为404 GET vs POST http协议中有很多方法GETPOSTPUTDELETEOPTIONS…其中最经常使用的方法是GETPOST至于其他方法就较为少见了如果遇到就现学吧。这里主要说明GET和POST的区别其中的依据来自 HTTP 方法GET 对比 POST。首先GET和POST都是明文传送两者都是不安全的使用这两个方法时我们的数据总是在网络上裸奔只是方式不同罢了 在之前的博客中我搭建了一个TCP网络通信模型由于http协议是基于tcp的所以我这里就直接改造这个模型实现一份服务端代码对浏览器客户端的请求做出响应 #include iostream #include string #include stdlib.h #include fstream #include unistd.h #include sys/wait.h #include sys/stat.h #include sys/types.h #include sys/socket.h #include arpa/inet.h #include netinet/in.husing namespace std;#define CRLF \r\n #define HOME_PAGE index.html #define ROOT_PAGE wwwroot #define SPACE string getPath(const string req) {// 请求行的获取size_t head_pos req.find(CRLF);if (head_pos string::npos)return ;string head req.substr(0, head_pos);// 资源路径的获取size_t path_start head.find(SPACE);size_t path_end head.rfind(SPACE);if (path_start string::npos || path_end string::npos)return ;// 获取资源地址string path head.substr(path_start 1, path_end - path_start - 1);// 如果地址为/默认访问家目录if (path[0] / path.size() 1) path HOME_PAGE;return path; }string readFile(const string path) {ifstream file(path);if (!file.is_open()) return 404;// 将资源的所有数据返回string line;string content;// 读取整份文件的内容while (file.peek() ! EOF){getline(file, line);content line;}return content; }// 先获取请求报头中的资源位置 // 读取该资源将其返回 void handlerRequest(int sock) {char buf[10240] {0};ssize_t r_ret read(sock, buf, sizeof(buf));if (r_ret 0)cout read fail endl;else if (r_ret 0)cout client quit endl;// 读取成功else{string req_str buf;// for testcout req_str endl;// 获取请求行中的资源string path getPath(req_str);// 读取客户请求的资源当前根路径的保存string resource_path ROOT_PAGE;resource_path path;// 获取文件资源string resource readFile(resource_path);string suffix ;// 获取文件的后缀size_t suffix_pos resource_path.rfind(.);if (suffix_pos ! string::npos)suffix resource_path.substr(suffix_pos);// 创建响应string response HTTP/1.1 200 OK\r\n;// 根据后缀为响应报头添加不同字段if (suffix .jpg) response Content-type: image/jpeg\r\n;else response Content-type: text/html\r\n;// 请求正文长度字段的添加response (Content-Length: to_string(resource.size()) \r\n);response \r\n;// 响应正文response resource;// 向客户端发送响应write(sock, response.c_str(), response.size());} }class tcpServer { public:tcpServer(uint16_t port, std::string ip ) : _ip(ip), _port(port) {}~tcpServer() {}void init(){// 创建套接字文件_listen_sockfd socket(AF_INET, SOCK_STREAM, 0);if (_listen_sockfd 0){std::cerr socket: fail std::endl;exit(-1);}// 填充套接字信息struct sockaddr_in local;local.sin_family AF_INET;local.sin_port htons(_port);_ip.empty() ? local.sin_addr.s_addr INADDR_ANY : inet_aton(_ip.c_str(), local.sin_addr);// 将信息绑定到套接字文件中if (bind(_listen_sockfd, (const struct sockaddr *)local, sizeof(local)) 0){std::cerr bind: fail std::endl;exit(-1);}// 至此套接字创建完成所有的步骤与udp通信一样// 使套接字进入监听状态if (listen(_listen_sockfd, 5) 0){std::cerr listen: fail std::endl;exit(-1);}// 套接字初始化完成std::cout listen done std::endl;}void loop(){// signal(SIGCHLD, SIG_IGN); // 设置SIGCHLD信号为忽略这样子进程就会自动释放资源// 创建保存套接字信息的结构体struct sockaddr_in peer;socklen_t peer_len sizeof(peer);// 接受监听队列中的套接字请求while (true){int server_sockfd accept(_listen_sockfd, (struct sockaddr *)peer, peer_len);if (server_sockfd 0){std::cerr accept: fail std::endl;continue;}std::cout accept done std::endl;// 提取请求方的套接字信息uint16_t peer_port ntohs(peer.sin_port);std::string peer_ip inet_ntoa(peer.sin_addr);// 打印请求方的套接字信息std::cout accept: peer_ip [ peer_port ] std::endl;// 使用孙子进程提供服务// grandparentpid_t id fork();// 祖父进程if (id 0){// 父进程pid_t cid fork();if (cid 0){// 关闭父进程exit(1);}// 孙子进程孤儿由1号进程管理handlerRequest(server_sockfd);}// 阻塞的回收进程资源waitpid(id, nullptr, 0);}} private:std::string _ip;uint16_t _port;int _listen_sockfd; };static void Usage(std::string proc) {std::cerr Usage:\n\t proc port std::endl;std::cerr example:\n\t proc 8080\n std::endl; }int main(int argc, char *argv[]) {if (argc ! 2){Usage(argv[0]);exit(0);}uint16_t port atoi(argv[1]);tcpServer svr(port);svr.init();svr.loop();return 0; }tcp通信的部分就不再赘述了。为防止僵尸进程该通信模型创建孙子进程为客户端提供服务而退出子进程使孙子进程成为孤儿进程由1号进程负责其资源的释放。服务端接收到客户端的请求会对该请求进行解析得到请求需要的资源路径服务端会将该资源返回给客户端。下面这份网页就是服务端默认返回给用户的资源 !– index.html – !DOCTYPE html htmlheadmeta charsetutf-8title测试/title /headbodyh3hello my server!/h3p我终于测试完了我的代码/pform action/a/b/c.html methodgetUsername: input typetext nameuserbrPassword: input typepassword namepasswdbrinput typesubmit valueSubmit/form /body/html这个网页中有一个表单客户可以提交它们的用户名和密码信息其中action标签表示将这些信息发送到指定网页上当然是客户端再向服务端发送一次请求了为了测试简单这里将信息发送到一个不存在的网页中我们只观察GET和POST的区别。 注意服务端接收到客户端的请求后会将客户端的请求打印出来方便我们的测试在浏览器的url中输入IP:port访问服务 我们输入用户名123密码456点击submit按钮这些数据会被发送到action指定的网页上并且方法是get
很正常因为网页不存在所以返回404。但是我们注意到输入的用户名和密码在url中以查询字符串的方式显示了出来。 将网页提交数据的方式修改为post重新运行服务重新连接到该服务输入用户名密码submit 可以看到用户名和密码没有在url中体现观察服务端打印的客户端请求可以看到这些信息出现在请求的正文部分。 总结一下GET将数据以查询字符串的方式拼接到url中而POST将数据以正文的方式置于请求中两者都是明文传输不同的只是POST较GET更隐蔽一些但两者在本质上都是不安全的。 http状态码 1xx信息状态码Informational接收的请求正在处理由于现在的网络响应快这样的状态码很少使用了2xx成功状态码Success请求正常处理完毕3xx重定向状态码Redirection将请求重定向到其他资源上4xx客户端错误状态码Client Error服务器无法处理请求请求非法5xx服务端错误状态码Server Error服务端运行出错 这些状态码也不需要具体记忆记住几个常用的就行了比如200(OK)404(Not Found)403(Forbidden)302(Redirect)504(Bad Gateway服务器访问上游服务器超时) void for_redirection(int sock) {char buf[10240] {0};ssize_t r_ret read(sock, buf, sizeof(buf));if (r_ret 0)cout read fail endl;else if (r_ret 0)cout client quit endl;// 读取成功else{string req_str buf;// 创建响应string response HTTP/1.1 302 Moved Temporarily\r\n;// 请求正文长度字段的添加response location: https://baike.baidu.com/item/302/878045?fraladdinrn;response \r\n;// 向客户端发送响应write(sock, response.c_str(), response.size());} }简单的将服务端的响应修改请求行修改为HTTP/1.1 302 Moved Temporarily\r\n然后在报头中添加location字段表示重定向的url如果服务端只提供以上服务的话不论客户端向服务端发送什么客户端都将跳转到location字段的url上。不过关于301和302的区别还是要注意一下的301是当前资源的永久性转移而302是资源的临时性转移。如果收藏夹中有一个url301了浏览器可能会将该url修改为新的url而302就不会 http常见header Content-type正文数据类型html/text,image/jpeg) Content-Length正文长度 Host告知服务端客户端要访问的资源在哪台主机上 User-Agenr声明用户的操作系统和浏览器版本信息 referer当前页面是从哪个页面跳转过来了 location配合3xx使用指定重定向的url客户端将访问该url Cookie存储用户一些数据用来进行会话的维持 header就是http中的报头字段以上展示的是字段中的key每一字段以key:value的方式呈现 最后还剩一个值得讲解的headerConnectionhttp1.0的Connection默认为closedhttp1.1的Connection默认为keep-alive一个为短连接一个为长连接。在互联网发展早期网页中的信息没有现在这么的密集一次http请求就可以获取完整的网页并呈现给客户。但是随着互联网的发展网页中的信息越来越多越来越复杂所以一次http协议无法获取完整的网页而http是基于tcp通信的多次http请求就代表这多次tcp的连接这样的做法势必会导致网页加载速度的下降由此http/1.1默认开启长连接只有一个网页的资源请求完成tcp连接才会关闭。关于长连接的深入学习具体实现可以阅读这篇博客 cookie session http有一个特性无状态即无法进行状态的保持每一次请求都是独立的。但在网页端浏览b站时登录账号过后每次访问b站都会保持你的登录信息其中的原理就与cookie有关 上图来自网络当然了首次登录b站时你还是需要输入你的账号密码的一旦选择登录浏览器就会将你的用户信息通过http协议传输到b站的服务端服务端认证成功确认该账号是有效的之后会生成一个cookie文件并发送一个set-Cookie的响应使客户端在磁盘或者内存上保持该cookie文件。之后客户端的http请求就会携带这份cookie文件服务端收到cookie文件后进行解析得到有效数据认证成功之后才会将特定的响应返回给客户端此时客户端看到的网页就是已经登陆账号的网页了。 如果cookie存储在磁盘上那么关闭浏览器甚至关机之后再打开该网页你的账号登录信息依旧能保持因为cookie文件在磁盘上除非你直接删除否则该文件会一直存在。但是cookie文件存储在内存浏览器中时关闭了浏览器注意不是关闭网页浏览器的进程资源被释放属于浏览器资源的cookie文件当然也被释放此时再打开浏览器登录信息就无法保持。如果只是把网站关闭浏览器没关闭再打开该网站登录信息依旧是能保存的因为cookie文件没有被释放。 但由于安全性的问题将cookie文件存储在客户端的做法。因为客户端容易遭到攻击或者说信息容易泄漏如果cookie被非法窃取cookie所有者的权益将会受到损害。所以现在主流的策略都是cookiesession将cookie文件存储在服务端Linux系统安全性极高。其主要实现是客户端提交登录信息服务端接收后在后台数据库上将这些信息存储起来然后生成一个唯一的id这个id可以理解为key数据库中存储的信息可以理解为value这就是一对key:value模型。服务端将id返回给客户端使客户端set-Cookie将该id值保存起来此后客户端的http请求都会带上该id值服务端收到id后找到其对应value就能解析出客户的信息也就能根据这些信息进行会话保持了。 但是cookiesession的方式同样是不安全的id值同样可以被窃取被非法利用。但是被窃取的只是一个id值你的具体信息没有得到泄漏具体信息存储在服务端的数据库中要攻击这样的数据库还是比较难的。这也是相对直接使用cookie的优势吧或者这么说http协议就是不安全的它的目的是通信的进行侧重于通信的实现至于安不安全就是另外一回事了因此不推荐用http进行私密数据的传输要进行这样的传输可以使用更侧重安全性的https协议