什么是HTTP协议?

HTTP协议是一个典型的应用层协议,是我们学习使用应用层协议的途径,接下来阿鲤就会详细的对HTTP协议做一个介绍

1:序列化和反序列化

2:网络数据传输格式

3:认识URL

4:urlencode编码&urldecode解码

5:HTTP协议格式

6:Cookie/Set-Cookie

7:一个简单的http模型搭建


一:序列化和反序列化

序列化:将数据对象按照指定的协议格式进行持久化存储/数据传输的过程

反序列化:将持久化存储/数据传输的二进制数据串按照指定协议解析出哥哥数据对象的过程

1:json序列化介绍https://blog.csdn.net/pupilxiaoming/article/details/80988013

二:网络数据传输格式

网络中数据传输格式分为

字符组织格式:以特殊字符进行间隔,比较节省空间。

结构化数据:使用结构体将数据结构化,这样的传输方式,反序列化块,使得更加敏捷的获取到数据。

三:认识URL

URL 即Uniform Resource Locator,统一资源定位符,也就是我们常说的网址,接下来给大家解析以下HTTP的格式;

http://username:password@baike.baidu.com/item/HTML/97406?fr=aladdin#ch

http:协议方案名称

username:password:用户名和密码

baike.baidu.com:服务器地址信息(ip地址(域名解析)和port号(默认80))

/item/HTML/97406:资源路径(实体资源(加载网页文件)or某个功能(登陆))

fr=aladdin:查询字符串(提供给服务器的信息,格式为key=val&key=val的型式)

ch:片段标识符,指向html标签

四:urlencode编码&urldecode解码

在UDL中所提交代请求字符串往往会带有特殊字符,这样就会导致数据的二义性,就会导致服务器解析失败,所以这就需要对这些特殊字符进行处理。所以就有了特殊字符的数据转换-urlencode

urlencode编码:将特殊字符转换成16进制的字符串,并使用%加以标注:

eg:对+的urlendoding编码
+的十进制十43转换成十六进制十2B  所一位 %2b

urldecode解码:将编码后的转换成为原有字符

五:HTTP协议格式

在这里首先给大家介绍一个抓包工具:fiddler(大家可以自行百度下载) 

使用这个工具作为网络传输中的代理,你就可以得到他们通信之间的每次的请求和响应的数据

 

HTTP协议格式如下

首行

头部

空行

正文

一:首行:

1:请求首行:请求首航中包含三条信息,\r\n作为结尾,格式如下:

请求方法 URL 协议版本\r\n

请求方法:

1:GET:主要用于获取实体资源,没有正文,所提交的数据放在URL的查询字符串中:但是URL的长度不能超过8k;

2:HEAD:相较于get,在响应信息中它只需要头部不需要正文

3:POST:主要向服务器提交表单数据,并且数据存放在正文中,安全性高于GET;

4:DELETE:请求删除指定资源

5:PUT:创建新的资源或替换请求负载目标资源表示

更多请求方法:HTTP中文开发手册

URL:见上文

协议版本:

HTTP/0.9:短链接,http在传输层使用的十tcp协议,tcp是面向链接的,发出请求,得到相应则通信结束关闭链接,之支持GET

HTTP/1.0:支持长链接,并且增加了请求方法及头部信息:POST/HEAD

HTTP/1.1:在支持长连接的基础上支持管线化传输;并且增加了更多的请求方法以及头部信息

HTTP/2:增加了更多的特性,增加了服务器主动向客户端推送信息。。。

2:相应首行:相应首行中包含三条信息,以\r\n结尾,格式如下

协议版本 相应状态码 状态码描述\r\n:

协议版本:HTTP/1.1....

相应状态码:告诉客户端针对这次请求,是一个什么样的处理结果 分别有以下五种类型

1xx:描述信息

2xx:正确处理完毕;200

3xx:重定向;301/302/303;比如我们在登陆时,验证没问题,然后告诉你让你去请求另一个资源

4xx:客户端错误;400/404

5xx:服务端错误;500/502/504

eg:

200:表示服务器已经成功接受请求,并将返回客户端所请求的最终结果

206:成功状态响应代码指示请求已成功并且主体包含所请求的数据范围(断点续传)

301:客户端请求的网页已经永久移动到新的位置,当链接发生变化时,返回301代码告诉客户端链接的变化,客户端保存新的链接,并向新的链接发出请求,已返回请求结果

302:Found临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI;

303:See Other查看其它地址。与301类似。使用GET和POST请求查看

304:客户端已经执行了GET,但文件变化。

400:Bad Request客户端请求的语法错误,服务器无法理解

404:请求求失败,客户端请求的资源没有找到或者是不存在。

500:服务器遇到未知的错误,导致无法完成客户端当前的请求。

502:服务器由于临时的服务器过载或者是维护,无法解决当前的请求

状态码描述:对于状态码的解释;比如200--正确处理,302-Found,404-Page Not Found

二:头部

头部由一个个key:val形式的键值对组成,每个键值对以\r\n结尾,键值对之间以\r\n作为间隔,描述本次的请求,是相应的关键信息eg:

Connection-本次请求是否是长连接  keep-alive(长连接)/close(短链接)

Connten-Length:用于描述正文长度,让对方接收数据时知道数据段长度

Cache-Control:max=age=0 (每次都更新);用于描述缓存的时长

Conntent-Type:用于描述正文的数据类型,让对方知道数据该如何处理

User-Agent:客户端描述信息,比如浏览器版本,等等。。

Accept:告诉对方我能接收怎样的数据;比如压缩格式,编码格式,语言等等

Referer:客户端告诉服务端我是从哪跳转过来的;比如使用百度进其他的网站。

Transfer-Encoding:传输方式,eg:chunked--分块传输--正文不会一次性全部传输,而是分多次传输,每次传输一块正文前先告诉对方这块正文有多长

Location:搭配3xx状态码使用,告诉对方资源重定向的新位置(让客户端请求新的位置)

Range:HTTP请求报头指示该服务器应返回的文档的一部分

Contrnt-Range:响应的 HTTP 报头指示其中一个完整的身体信息的部分消息所属(Range和Content-Range常用于断点续传)

三:正文

传输的信息

六:Cookie/Set-Cookie

因为早期的HTTP协议建立的链接都是短链接,即在一次请求和相应结束后就会断开链接,所以就会出现以下问题;

比如我们在网购,买东西时,我们需要将我们购买的东西请求发给服务器,让服务器帮我们处理,在处理完之后因为短连接的问题请求就会退出,这就使得我们想要继续买东西时得重新发起请求。而Cookie/set-Cookie就是用来解决这个问题的。

每次客户端登陆,服务器就会为其创建一个session(会话),会话中保存着当前会话信息,客户端认证信息等等,然后将这些信息存储到数据库中,在登陆成功响应时使用Set-Cookie告诉对方会话id是多少,客户端在收到这相应之后,将id保存在Cookie中;

在下次客户端发起请求的时候,就会自动将Cookie发送给服务器,服务器在收到这个Cookie之后去除信息session_id,通过这个id在数据库中找到相应的会话信息,并打开。

七:一个简单的http服务器模型搭建

HTTP服务器其实就是一个tcp服务器,其在传输层使用的是tcp协议,只不过在应用层的数据沟通是采用HTTP协议格式的。直接上代码了,会有详细的注释

HTTP.cpp:

/*********************************************************
1:搭建一个tcp服务器,等待客户创建套接字
2:客户端连接来,服务端创建新的套接字
3:通过这个新的套接字接收数据,传输数据
**********************************************************/
#include<stdlib.h>
#include"tcpsocket.hpp"//自己封装的tcp通信
#include<sstream>

int main(int argc, char *argv[])
{
	if(argc != 3)
	{
		std::cout << "plase scanf ./http.cpp ip port\n";
		return false;
	}

	TcpSocket lst_sock;
	lst_sock.Socket();//创建tcp套接字

	std::string ip = argv[1];
	uint16_t port = atoi(argv[2]);

	lst_sock.Bind(ip, port);//绑定
	lst_sock.Listen();//开始监听

	TcpSocket newsock;

	while(1)
	{
		bool ret = lst_sock.Accept(newsock);//获取新连接
		if(ret == false)
		{
			continue;
		}

		std::string buf;//接收缓冲区
		ret = newsock.Recv(buf);//接收数据
		if(ret == false)
		{
			newsock.Close();
			continue;
		}
		
		std::cout << "http req:[" << buf << "]\n";//打印接受的数据

		std::string body = "<html><body><h1>hello world</h1></body></html>";//正文
		std::string blank = "\r\n";//空行
		std::stringstream header;//头部信息
		header << "Content-Length: "<< body.size() << "\r\n";//正文长度,将数据转换成字符串
		header << "Content-Type: text/html\r\n";//显示的数据类型
		
		std::string first = "HTTP/1.1 200 OK\r\n";//响应首行
		newsock.Send(first);//发送首行信息
		std::string tmp(header.str());
		newsock.Send(tmp);//发送头部
		newsock.Send(blank);//发送空行
		newsock.Send(body);//发送正文

		newsock.Close();//关闭,短链接
	}

	lst_sock.Close();//关闭套接字
	return 0;
}

运行情况:

1:

 

2: 

 

3: 

tcpsocket.cpp

 

/******************************************************************************
 * 描述:封装一个tcpsocket类,向外提供接口,能够实现客户端服务端编程流程
 *流程:1:创建套接字
 *      2:绑定地址信息
 *      3:开始监听/发起连接请求
 *      4:获取已完成连接
 *      5:发送数据
 *      6:接收数据
 *      7:关闭套接字     
 ******************************************************************************/

#include<unistd.h>
#include<iostream>
#include<string>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#define CHECK_RET(q) if(q == false){return -1;}

class TcpSocket
{
    int m_socket;

public:

    TcpSocket():
        m_socket(-1)
    {}

    ~TcpSocket()
    {
        Close();
    }

    bool Socket()//创建套接字
    {
        m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(m_socket < 0)
        {
            std::cerr << "socket creat error" << std::endl;
            return false;
        }
        return true;
    }

    bool Bind(std::string &ip, uint16_t port)//套接字绑定
    {
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        socklen_t len = sizeof(sockaddr_in);
        int ret = bind(m_socket, (struct sockaddr*)&addr, len);
        if(ret < 0)
        {
            std::cerr << "bind error \n";
            return false;
        }
        return true;
    }

    bool Listen(int backlog = 5)//开始监听//backlog:最大并发连接数
    {
        int ret = listen(m_socket, backlog);
        if(ret < 0)
        {
            std::cerr << "listen error\n";
            return false;
        }
        return true;
    }

    bool Connect(std::string &ip, uint16_t port)//客户端发起连接请求
    {
        sockaddr_in addr;//服务端地址
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(&ip[0]);
        socklen_t len = sizeof(struct sockaddr_in);
        int ret = connect(m_socket, (sockaddr*)&addr, len);
        if(ret < 0)
        {
            std::cerr << "connect erroe\n";
            return false;
        }
        return true;

    }

    void SetFd(int fd)//获取新连接,因为m_socket私有;
    {
        m_socket = fd;
    }

    bool Accept(TcpSocket &newsock)//服务端获取已完成的新连接
    {
        sockaddr_in addr;//并不需要得到客户端的地址,但是也可以获得
        socklen_t len = sizeof(sockaddr_in);
        int fd = accept(m_socket, (sockaddr*)&addr, &len);
        if(fd < 0)
        {
            std::cerr << "accept error\n";
            return false;
        }
        newsock.SetFd(fd);//将新连接的套接字操作句柄给newsock
        return true;
    }

    bool Send(std::string &buf)//发送,若发送到过大,先发送一部分;所以要判断是否发送完
    {
        int ret = send(m_socket, &buf[0], buf.size(), 0);
        if(ret < 0)
        {
            std::cerr << "send error\n";
            return false;
        }
        return true;
    }

    bool Recv(std::string &buf)//接收数据,没有数据会阻塞
    {
        char tmp[4096] = {0};
        int ret = recv(m_socket, tmp, 4096, 0);
        if(ret < 0)
        {
            std::cerr << "recv error\n";
        }
        else if(ret == 0)//若返回值等于0则表示连接已断开
        {
            std::cerr << "peer shutdown\n";
            return false;
        }
        buf.assign(tmp,ret);//拷贝
        return true; 
    }

    bool Close()
    {
        if(m_socket >= 0)//0:标准输入(在标准输入被关闭时,会等于0;(最小位使用))
        {
            close(m_socket);
            m_socket = -1;
            return true;
        }
        return false;
    }

};

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值