Linux--深入解析HTTP协议

本文介绍了HTTP协议的基本概念,它作为应用层协议用于规定数据交换的约定。HTTP协议包括请求和响应两部分,详细阐述了请求行、请求报头、请求正文和响应行、响应报头、响应正文的结构。此外,还提及了常见的HTTP状态码,并展示了如何在CentOS 7上实现一个简单的HTTP服务器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是HTTP协议,HTTP协议有什么作用?

  • 首先HTTP协议属于应用层协议,应用层的协议就是我们 程序员自己定的,但是,如果每次都要我们程序员自己定协议,又麻烦而且容易出错(自己什么水平自己应该有点数( ̄_ ̄|||)),最重要的是有大佬已经定义了一些现成的(HTTP,DNS等),所以大多时候我们都是直接拿来用,而应用层的作用就是双方达成某种约定,一端按照约定的协议发送数据,另一端按照约定来解析数据,保证接收数据的一段可以正确解析数据,这就是应用层的作用,也是HTTP协议的作用之一。

HTTP协议的格式

HTTP协议分为HTTP请求和HTTP响应

HTTP请求:
这里写图片描述
说明:

  • 第一部分为第一行,为请求行,请求行以空格分为三个区域,第一个区域叫请求方法,常见方法为POST,GET方法;第二个区域指名想要访问的资源(通常以路径的形式呈现)(网页也属于文件);第三个区域表示HTTP协议行的版本(常见1.0/1.1).
  • 第二部分从第二行开始,一直到空前叫请求报头(Header),每一行都代表一个特定的含义,每一行格式都是name:value。
  • 第三部分为空行,目的是为了将请求正文和请求行、请求报头分隔开(读到空格表示请求行和请求报头读完)。
  • 第四部分问请求正文(Body可以为空,请求报头中的content-length表示正文大小)

HTTP响应:
这里写图片描述
说明:

  • 第一部分为第一行,为响应行,也分为三个区域,用空格间隔,第一个区域表示协议版本;第二个区域表示状态码(200–表示请求正常处理完毕、404–表示无法处理请求等常见状态码);第三个区域表示状态码解释。
  • 第二部分从第二行开始,一直到空前叫请求报头,每一行都代表一个特定的含义,每一行格式都是name:value。
  • 第三部分为空行,目的是为了将请求正文和请求行、请求报头分隔开(读到空格表示请求行和请求报头读完)。
  • 第四部分为正文(Body),空⾏后⾯的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有⼀个Content-Length属性来标识Body的⻓度。

常见状态码介绍:
这里写图片描述

实现一个简单的HTTP服务器

#include <sys/wait.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>

void service(int sock, char* ip, int port)
{
    char buf[10240];//用一个足够大的缓冲区一次性将数据读完
    buf[0] = 0;
    read(sock, buf, sizeof(buf));
    const char* response = "HTTP/1.0 200 OK\n\n<html><head>hello world</head></html>\n";
    write(sock, response, strlen(response));
}


int StartUp(int port)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock < 0)
    {
        perror("socket");
        exit(2);
    }

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htonl(INADDR_ANY);
    local.sin_port = htons(port);

    if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
    {
        perror("bind");
        exit(3);
    }

    //设置为监听状态
    if(listen(sock, 5) < 0)
    {
        perror("listen");
        exit(4);
    }

    return sock;
}

// ./myHttp 0 9090
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        printf("Usage: %s [ip] [port]\n", argv[0]);
        exit(1);
    }

    int listen_sock = StartUp(atoi(argv[2]));
    struct sockaddr_in peer;
    int len = sizeof(peer);
    socklen_t new_sock = -1;
    daemon(0, 0);
    while(1)
    {
        if((new_sock = accept(listen_sock, (struct sockaddr*)&peer, &len)) < 0)
        {
            printf("Connect faily\n");
            continue;
        }

        char ipBuf[24] = {0};
        inet_ntop(AF_INET, (struct sockaddr*)&peer, ipBuf, sizeof(ipBuf));
        int p = ntohs(peer.sin_port);
        printf("get a new connect: [%s %d]\n", ipBuf, p);

        int id = fork();
        if(id == 0)//child
        {
            close(listen_sock);
            if(fork() > 0)
            {
                close(new_sock);
                exit(0);
            }

            service(new_sock, ipBuf, p);
            close(new_sock);
            exit(0);
        }

        else if(id > 0)//father
        {
            close(new_sock);//因为父进程不通信
            waitpid(id, NULL, 0);
            continue;
        }

        else
        {
            perror("fork");
            continue;
        }
    }
}

测试: centos 7

首先关闭防火墙(临时关闭):

//临时关闭
systemctl stop firewalld
//禁止开机启动
systemctl disable firewalld
Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.

运行服务器
这里写图片描述
在浏览器上输入自己的ip地址:
这里写图片描述
结果如下:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值