http协议--超文本传输协议(应用层)

一.自定制协议
二.HTTP协议(超文本传输协议)
三.HTTP协议格式
四.简单的HTTP服务器

应用层:负责应用程序之间的数据沟通(指定数据格式)

一.自定制协议:

使用boost库当中的切割函数split

#include <boost/algorithm/string.hpp>
boost::split(type,select_list,boost::is_any_of(""),boost::token_compress_on/off)
type:类型是vector<std::string>类型,保存切割完成之后的字符串
select_list:待切割的字符串,可以直接传入string对象
boost::is_any_of:告诉spilt函数按照什么样地字符进行切割,单个字符支持,支持字符串
boost::token_compress_on/off:
当是“on":对于多个连续的分割字符当成1个处理
当是”off":有多少个分割字符就按照多少个分割符来处理
5 int Sum(std::string Data)
  6 {
 15     std::vector<std::string> output;
 16     std::string split_char ="+";
 17     boost::split(output,Data,boost::is_any_of(split_char),boost::token_compress_off);
 18     //int sum=atoi(output[0].c_str())+atoi(output[1].c_str());//将字符串转整型
 19     int sum_number=0;
 20     for(size_t i=0;i<output.size();i++)
 21     {
 22         sum_number+=atoi(output[i].c_str());
 23 
 24     }
 25     return sum_number;
 26 
 27 }

存在粘包问题
解决方法:
定长包头+数据(有效载荷)+分隔符

1.双方约定数据格式(struct),单数存在多个结构体结构,服务端不知道客户端先发那哪个结构体的大小—>可以在最前面添加这个数据结构数据长度
程序从接收缓冲区当中接收数据时,需要先探测接收,对头部信息进行解析,解析到数据长度,进而真正接收的时候,就知道数据多长了–效率问题:高并发的情况
2.定义包头当中可以获取当前数据的长度,而分隔符可以找到下一条数据的起始位置(下一条数据的包头)
在这里插入图片描述
3.分割符的原理:定义数据边界。
分隔符不一定是一个字节的字符,可以是一个字符串,通常情况下工业都是使用\r\n(0D 0A)来分割

结构化传输(发送数据时将这个结构体按照一个规则转换成字符串,接收到数据的时候再按照相同的规则把字符串转化结构体)

序列化:将对象转化成字节序列的过程。
在传输数据的时候,按照数据结构对象当中变量的组织格式进行传输

send(sockfd,Data,sizeof(Data),0);

反序列化:将字节序列转化为对象的过程。
在接收数据的时候,按照数据结构对象当中变量的组织格式进行接收

struct Data d;
recv(sockfd,&d,sizeof(struct Data),0);

二. HTTP协议(超文本传输协议)

在这里插入图片描述

扫描二维码关注公众号,回复: 11552280 查看本文章

带层次的文件路径:资源路径,"/"并不是服务端机器的实际根目录,而是一个逻辑根目录。

查询字符串:querystr:给服务端提交的数据,提交的单个数据是key=value的形式,多个提交的数据使用&向连接在value当中会对提交的数据进行urlencode(url编码),

由于可能在value当中存在特殊字符,这些特殊字符本身就具有普遍的含义(eg:+加号的符号);编码过程就是将字符转化成为16进制的字符串来进行传输,为了告诉服务器,当前的字符串是通过转义的,在转义后的字符串前面加上%用来区分

转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式

片段标识符:表示当前页面(资源)被浏览器加载之后,定位在什么位置

urldecode就是urlencode的逆过程

三.HTTP协议格式**

HTTP请求

在这里插入图片描述

请求首行\r\n

请求方法+url+协议版本\r\n

请求方法:
Get:从服务端获取数据(提交的数据在url当中,数据是由长度限制的,长度和浏览器是强相关的
Post:向服务端提交数据(提交的数据在正文当中,长度是没有限制的)
「Post方法比Get方法更私密。」
协议版本 HTTP/0.9 HTTP/1.0 HTTP/1.1 HTTP/1.2 版本之间的特性不一样

请求头部

具有多行数据,每行数据都是一个key:value的形式,每行数据的后面都存在\r\n
Content-Length:正文长度,防止粘包
Content-Type:正文的编码格式

text/html:HTML格式
text/plain:纯文本格式
text/png:图片格式
application/json:JSON数据格式
application/msword:word文档格式
application/pdf:pdf格式

referer:当前的页面是从哪一个页面跳转过来的
Host:客户端告知服务器,所请求的资源在哪个主机的哪个端口上
Cookie:向服务器提交浏览器本地保存的认证信息,认证信息是之前登陆服务器的时候,服务器返回回来的
Set-Cookie:告诉浏览器保存哪一些会话认证信息
Transfer-Encoding:针对正文而言,支持分块传输;
Loctaion:与重定向搭配使用,服务器告诉浏览器,刚才请求的资源应该从哪一个地址上重新申请
User-Agent:声明用户的操作系统和浏览器的版本信息
Connection:keep-alive 保持长连接「http协议应用协议,传输层使用的是tcp协议,早期http是无状态的协议,使用的短连接

空行
正文 数据信息(允许为空字符串)

HTTP响应

在这里插入图片描述

响应首行

协议版本 状态码 状态码解释\r\n

状态码:
1xxx:接收到的请求正在处理
2xxx:请求处理正常完毕(200 OK)
3xxx:重定向的状态,表示浏览器还是需要进行附加操作,才能完成刚才的请求(302 临时重定向)
4xxx:服务器当前无法处理这个请求(404 Page Not Found)
5xxx:服务器出路出错了(505 Bad Getway 坏的网关)

响应头部

请求的属性,冒号分割的键值对;每组属性之间使用\n分隔(遇到空行Header部分结束)

空行
正文

空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中.

总结:
1.http协议是一个应用层协议,在传输层使用的是tcp协议,在网络层使用的是ip协议。
2.http本身为了处理大量的请求,设计是短连接的(发送一个数据之后接收到响应之后就将连接断开了)。
3.目前http服务已经支持长连接。
4.http是没有加密版本的http协议,加密版本可以使用https,s代表ssl:非对称加密
5.http协议是无状态协议,只负责接收到请求,返回相应的处理结果,并不会记录上一层请求的数据。

四.简单的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>
void Usage() 
{
printf("usage: ./server [ip] [port]\n");
}

int main(int argc, char* argv[]) {
if (argc != 3) 
{
Usage();
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;
}

猜你喜欢

转载自blog.csdn.net/unique_IT/article/details/107014889