应用层协议
负责应用层程序之间的数据沟通(应用层的协议是程序员自己定制的)
自定制协议
只要保证, 一端发送时构造的数据, 在另一端能够正确的进行解
析, 就是ok的. 这种约定, 就是 应用层协议。
自定制协议中的相关概念:
- 序列化:将数据对象按照指定协议组织成为可持久化存储/数据传输的二进制数据串
- 反序列化:将二进制数据串按照指定协议进行解析得到各个数据对象
HTTP协议
HTTP协议即超文本传输协议。
认识URL
我们常说的网址就是URL(统一资源定位符)
协议方案名:用户名:用户密码@服务器地址:服务器端口号/请求的资源路径名称?查询字符串#片段标识符
- 域名:一种IP地址的表示方式–但是域名还是需要转换为服务器的IP地址
- 查询字符串:客户端给服务端提交的数据,需要进行url编码(对特殊字符进行转)由一个个键值对组成,并且键值对是以key=val的形式,键值对之间以&进行间隔
- #片段标识符:一个标签,直接转到网页的某个位置
URL的转码和解码
- urlencode:将特殊字符的每一个字节都转换为16进制数字的字符串,并且为了表示两个字符串经过了url编码,因此在编码后的字符前加上%符号。
- urldecode:当在查询字符串中遇到%符号,则认为紧跟其后的两个字符串进行解码,将两个字符转换为数字,第一个数字左移4位,加上第二个字符
HTTP协议格式
HTTP请求
- 首行: [方法] + [url] + [版本]
- Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
- Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;
HTTP响应
- 首行: [版本号] + [状态码] + [状态码解释]
- Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
- Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中.
HTTP的方法
HTTP的状态码
常用状态码及解释:
200响应正确
301永久重定向 302临时重定向 303查看其他地址
403拒绝请求 404请求的资源没有找到
500服务器内部错误 502网关错误(代理或者网关返回)
HTTP常见Header
- Content-Type: 数据类型(text/html等)
- Content-Length: Body的长度
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
- User-Agent: 声明用户的操作系统和浏览器版本信息;
- referer: 当前页面是从哪个页面跳转过来的;
- location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
- Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
模拟实现HTTP服务器
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("usage: ./server [ip] [port]\n");
return 1;
}
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return 1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
perror("bind");
return 1;
}
ret = listen(fd, 10);
if (ret < 0) {
perror("listen");
return 1;
}
for (;;) {
struct sockaddr_in client_addr;
socklen_t len;
int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len);
if (client_fd < 0) {
perror("accept");
continue;
}
char input_buf[1024 * 10] = {0};
ssize_t read_size = read(client_fd, input_buf, sizeof(input_buf) - 1);
if (read_size < 0) {
return 1;
}
printf("[Request] %s", input_buf);
char buf[1024] = {0};
const char* hello = "<h1>hello world</h1>";
sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello), hello);
write(client_fd, buf, strlen(buf));
}
return 0;
}