浅谈 HTTP

URL格式

URL即'网址'

  • 服务器地址实际上是指ip地址,这里用域名表示,便于记忆
  • http服务器端口号默认80,可以不使用80
  • 带层次的文件路径不同,访问服务器上的文件就不同
  • 查询字符串 : 都是以键值对的方式存储的, 键和值之间使用等号分隔,每一组键值对之间用 & 分隔
  • 片段标识符 : 表示要跳转到本页面的某一部分

urlencode和urldencode

即 '编码' 和 '解码'
我们可以看到,在上面的url格式中,有一些我们固定使用的字符,比如 / ? : 等,这些字符在url中有着特殊的含义,所以我们不可以随便的使用他
如果某个参数中带有这样的特殊字符,我们就必须对他进行转义

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

例如百度搜索C++时:

我们可以看到, + 号被转化为了 %2B


HTTP报文

用于HTTP协议交互的信息我们称之为报文,客户端的HTTP报文我们叫做请求报文,服务器端的HTTP报文我们叫做响应报文
HTTP报文大致可分为 报文首部,Header,空行,Body

通过下面我们使用抓包工具抓的HTTP包可以看出来, http协议是纯文本协议,行文本

HTTP请求报文

GET请求 : 

首行:    以下三部分信息都用空格分隔
  • GET : http协议的方法,表示浏览器要从服务器上获取某一个数据
  • http://baidu.com/ : 一个url,表示具体访问服务器上哪一个资源
  • HTTP/1.1 : 当前HTTP版本号
Header:    以键值对的方式存储,每一对键值对换一行,键和值以 [冒号][空格] 分隔(请求的Header字段有很多,这里主要讲解一下上面出现过的字段)
  • Host : 表示访问主机名,也就是ip地址的别名,即域名
  • Connection: 逐跳首部,连接的管理
  • User-Agent : 包含操作系统信息和浏览器信息
  • Accept: 用户代理可处理的类型
  • Accept-Encoding: 优先的内容编码
  • Accept-Language: 优先的语言(自然语言)
  • Cookie : 用来保存一个从服务器返回回来的字符串,并且保存到本地,下一次访问同一个网站,会将这个信息带回去
最常见的保存的是用户身份信息 : 例如用户登录信息,一旦在首页登陆过一次,访问其他页面就不需要登录
空行 : Header的结束标志


POST请求:
  • 首行 : [方法]+[url]+[版本]
  • Header :
Content-Type : 如果最后面是 -from-urlencoded,那么body的格式就和querystr一致
Content-Length : 表示Body一共占多少字节
Referer : 表示当前页面是从哪一个页面跳转过来的
  • 空行 : 表示Header结束
  • Body :
格式很有可能和querystr格式一样,主要取决于Header中的Content-Type ( querystr :  键值对形式存储,键值对之间用 & 隔开,键和值之间用 = 相连)

GET和POST请求的区别 : POST有Body,并且在Header中用Content-Type和Content-Length来表示Body的类型和大小

HTTP响应报文


首行 :     以空格分隔
  • [版本号] + [状态码] + [状态码解释]
Header : 以键值对的方式存储,每一对键值对换一行,键和值以 [冒号][空格] 分隔
  • Sever: HTTP服务器安装信息
  • Content-Type : text/html 表示body的格式是一个xml的文本格式; charset说明字符集为utf-8
  • Content-Length : Body部分的字节数
  • Vary: 代理服务器缓存的管理信息
  • Location: 令客户端重定向到指定的URL
  • Set-Cookie : 和请求中的Cookie对应.服务器利用Set-Cookie向客户端返回用来表示身份信息的的字符串,下次访问时就会重新带入,避免再次登录.
Cookie 是以域名为维度进行存储;
Cookie 的存储能力有限,浏览器默认4k,所以一般只保存身份标识,避免网络交互过程中占用过多的带宽;
空行 : Header结束标志
Body :
  • 浏览器见到这一部分内容,就会按照Content-Type的格式展示出来.

HTTP方法


方法说明支持的HTTP协议版本
GET获取资源1.0 / 1.1
POST传输实体主体1.0 / 1.1
PUT传输文件1.0 / 1.1
HEAD获得报文首部1.0 / 1.1
DELETE删除文件1.0 / 1.1
OPTIONS询问支持的方法1.1
TRACE追踪路径1.1
CONNECT要求用隧道协议连接代理1.1
LINK建立和资源自检的联系1.0 
UNLINK断开连接关系1.0 
1.0 是一个基于请求-响应的短连接


HTTP状态码


 类别原因短语经常见到的状态码描述
1XXInformational(信息性状态码)接收的请求正在处理  
2XXSuccess(成功状态码)请求正常处理完毕200 OK 请求成功(其后是对GET和POST请求的应答文档。)
3XXRedirection(重定向状态码)需要进行附加操作以完成请求
302 Found
所请求的页面已经临时转移至新的url。
4XXClient Error(客户端错误状态码)服务器无法处理请求
404 Not Found
服务器无法找到被请求的页面。
5XXServer Error(服务器错误状态码)服务器处理请求出错
504 Gateway Timeout
网关超时。

实现简单地HTTP服务器

这里是一个最简易版本的http服务器,不管客户端发出什么请求,服务器都相应一个hello world 页面
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>

int ServerInit(short port)
{
    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 = htonl(INADDR_ANY);
    addr.sin_port = htons(port);
    int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
    if(ret < 0)
    {
        perror("bind");
        return -2;
    }
    ret = listen(fd,3);
    if(ret < 0)
    {
        perror("listen");
        return -3;
    }
    return fd;
}

void* ThreadEntry(void* arg)
{
    int* pnew_sock = (int*)arg;
    int new_sock = *pnew_sock;

    // 构造一个足够大的响应空间,获取HTTP请求
    char buf[1024 * 10] = {0};

    read(new_sock,buf,sizeof(buf)-1);
    printf("[Request] %s\n",buf);

    // 构造HTTP响应
    const char* first_line = "HTTP/1.1 200 OK\n"; //此处构造http响应首部
    const char* blank_line = "\n"; //header和body之间的空行
    const char* body_line = "<html><h1>Hello World<h1></html>"; //此处构造Body,最简单的页面
    char header_buf[128] = {0};
    //构造header,这里仅包含body的类型和长度
    sprintf(header_buf,"Content-Type: text/html;\nContent-Length: %lu\n",strlen(body_line));

    write(new_sock,first_line,strlen(first_line));
    write(new_sock,header_buf,strlen(header_buf));
    write(new_sock,blank_line,strlen(blank_line));
    write(new_sock,body_line,strlen(body_line));

    close(new_sock);
    return NULL;
}

int main(int argc,char* argv[])
{
    if(argc != 2)
    {
        printf("Usage : ./http_server [port]\n");
        return 1;
    }
    // 创建 listen_sock
    int listen_sock = ServerInit(atoi(argv[1]));
    if(listen_sock < 0)
    {
        perror("ServerInit Faild\n");
        return 2;
    }
    printf("ServerInit OK\n");

    // 进入事件循环
    while(1)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int new_sock = accept(listen_sock,(struct sockaddr*)&peer,&len);
        if(new_sock < 0)
        {
            perror("accept");
            return 3;
        }

        // 使用线程完成多用户可访问模式
        pthread_t tid;
        pthread_create(&tid,NULL,ThreadEntry,(void*)&new_sock);
        pthread_detach(tid);
    }
    return 0;
}
执行指令:  ./http_server 9090
演示结果:


我们接收到的http请求

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值