目录
URL
HTTP协议请求与响应格式
请求
响应
重定向
HTTP常见header
最简单的HTTP服务器
测试
HTTP(超文本传输协议)定义了客户端(如浏览器)如何和服务器进行通信的超文本
客户端向服务器发送请求,服务器收到请求,处理后给客户端返回响应。
HTTP是一个无连接无状态协议,也就是说每次都需要重新建立连接,且服务器不会保存客户端状态。
URL
我们平时使用的网址其实就是URL
有些特殊字符已经被当作特殊意义理解了,例如上文中的@,#,?
如果某个参数中需要这些特殊意义的字符,我们就需要对这些字符进行转义
转义规则如下
将需要转码的字符转为 16 进制, 然后从右到左, 取 4 位(不足 4 位直接处理), 每 2 位
做一位, 前面加上%, 编码成%XY 格式
HTTP协议请求与响应格式
请求
请求报头是一个键值对,我们后面会放
最常见的是GET与 POST方法
GET:用于请求 URL 指定的资源
特性: 指定资源经服务器端解析后返回响应内容
如上文,就是请求'/'的资源(相对于服务器来说的)
至于请求的资源我们在后面的响应里面会写
POST:用于传输实体的主体, 通常用于提交表单数据
特性: 可以发送大量的数据给服务器, 并且数据包含在请求体中
当然了,以上两个最显著的区别就是,
GET方法,长度有限,并且会将我们请求的资源放在网址后面
POST方法则会隐藏我们请求的资源(不会放在网址后面)
响应
响应和请求很相似,只不过第一行改成了状态码和状态码描述
响应正文就是我们的网页代码,我们就不写了,放一个简单的hello world
状态码则是我们访问网页发送请求,后端根据请求判断网页访问的情况(不能乱填,浏览器会根据状态码判断网页是否正常)
比如说可以正常访问,网址换了(临时或永久),再或者网页关了之类的
重定向
网页重定向就是说网页换了个地址,比如说原来可能叫做4399.com,后来改名叫7k7k.com
我们有永久重定向(301)和临时重定向(其他30系的状态码)
当然了,重定向依赖的选项(键)为'Location'
我们在响应报头中填入键值对"Location:url(新的地址)",那么客户端就会根据我们时永久重定向还是临时重定向来决定是否需要缓存新的url
如果说是永久重定向,那么就会缓存新的url,每次访问原url就会直接去访问缓存的url
如果说是临时重定向则不会缓存
HTTP常见header
Content-Type: 数据类型(text/html 等)
Content-Length: Body 的长度
Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
User-Agent: 声明用户的操作系统和浏览器版本信息;
Referer: 当前页面是从哪个页面跳转过来的;
Location: 搭配 3xx 状态码使用, 告诉客户端接下来要去哪里访问;
Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
Connection:是否保持长连接(keep-alive表示希望保持,close表示请求/响应完成后关闭TCP连接)
最简单的HTTP服务器
#include <iostream>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
const int default_port = 8080;
int main()
{int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd < 0){std::cout << "sock创建失败" << std::endl;exit(0);}struct sockaddr_in addr;addr.sin_addr.s_addr = INADDR_ANY;addr.sin_family = AF_INET;addr.sin_port = htons(default_port);//这里要记得转网络int opt=1;setsockopt(fd,SOL_SOCKET,SO_REUSEADDR |SO_REUSEPORT,&opt,sizeof(opt));int n = bind(fd, (struct sockaddr *)&addr, sizeof(addr));if (n < 0){std::cout << "bind失败:" <<strerror(errno)<< std::endl;exit(0);}n = listen(fd, 5);if (n < 0){std::cout << "listen失败" << std::endl;exit(0);}while (true){struct sockaddr_in accept_addr;socklen_t len = sizeof(accept_addr);std::cout << "准备accept" << std::endl;int sockfd = accept(fd, (struct sockaddr *)&accept_addr, &len); // 我们这里只做演示,接收完一个,返回后就关掉std::cout << "accept成功" << std::endl;if (sockfd < 0){std::cout << "accept失败" << std::endl;exit(0);}char buffer[1024];int length = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (length < 0){std::cout << "接收失败" << std::endl;exit(0);}buffer[length] = 0;printf("接收到request:\n%s\n", buffer);char outbuffer[1024];const char *hello = "<h1>hello world</h1>";snprintf(outbuffer, sizeof(outbuffer), "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello), hello);send(sockfd, outbuffer, strlen(outbuffer), 0);printf("发送响应:\n%s\n",outbuffer);close(sockfd);}close(fd);
}