Linux基础 (十六):HTTP 协议与 web 服务器

         本篇博客,我们将共同探索HTTP协议的工作原理,理解它是如何在客户端和服务器之间传递信息的。我们将深入研究HTTP请求和响应的结构,了解状态码、头部信息和常见的HTTP方法。同时,我们还会讨论HTTPS的安全机制,了解如何保护数据的传输安全。

目录

一、浏览器与服务器通信过程(面试)

二、HTTP 请求报头

2.1 HTTP 的请求报头结构

2.2 HTTP 的请求方法

三、HTTP 应答报头

3.1 HTTP 的应答报头结构

3.2 HTTP 的应答状态

四、Web 服务器的 C 语言实现(了解)


一、浏览器与服务器通信过程(面试)

          HTTP 协议(超文本传输协议,不仅可以传输文本文件 还可以传视频,图片,声音 是一种应用层协议,主要用于浏览器与 web 服务器通信使用,而 HTTP 协议在传输层使用的还是 TCP 协议。浏览器需要和 web 服务器三次握手建立连接后,才可以发送 HTTP 请求报文,服务器收到请求报文后,向浏览器回复 HTTP 应答报文。浏览器向服务器发起连接前,需要得到服务器的 IP 及端口。用户在浏览器中通常只输入网址(网站域名),浏览器会通过 DNS 服务查询获取到服务器的 IP 地址。对于端口来讲, 使用 HTTP 协议的程序一般默认使用 80 端口。整个过程如下图所示:

长连接:

         浏览器服务器建立连接后,如果两次以上的请求复用同一个 TCP 连接,则称之为长连接。

短连接:

         如果浏览器发送一次请求报文,服务器回复一次应答就断开连接,下次交互再重新进行三次握手建立连接,那么就被称作短连接。使用长连接显然是更好一些,可以减少网络中的 同步报文,也使得服务器的响应速度变快。

注意:

       1024以内的端口只有管理员才能使用,因此,必须切换到管理员身份。

常见的 web 服务器有:

◼ Apache: 简单、速度快、性能稳定,并可做代理服务器使用

◼ IIS(Internet Information Server):安全性、强大、灵活

◼ Nginx:小巧而高效,可以做高效的负载均衡反向代理

◼ Tomcat:技术先进、性能稳定、免费 

二、HTTP 请求报头

2.1 HTTP 的请求报头结构

       HTTP 请求报头(HTTP Request Headers)是 HTTP 请求消息的一部分,包含关于客户端、请求资源以及请求本身的附加信息。请求报头帮助服务器了解请求的性质和客户端的能力,从而做出适当的响应

HTTP 请求报文段示例:

  1. Request Line (请求行): 包含HTTP方法(如GET、POST)、请求的资源路径、HTTP版本。 
  2. Host: 指定请求的目标主机和端口(IP地址或者网站)。
  3. User-Agent: 指定发出请求的客户端软件信息。
  4. Accept: 指定客户端可处理的内容类型。
  5. Accept-Language: 指定客户端希望的语言。
  6. Accept-Encoding: 指定客户端支持的内容编码类型(压缩方法)。
  7. Connection: 指定是否需要保持连接。

2.2 HTTP 的请求方法

      HTTP 请求方法(HTTP Request Methods)是客户端用来向服务器指明所要执行的操作的。

三、HTTP 应答报头

3.1 HTTP 的应答报头结构

        HTTP 应答报头(HTTP Response Headers)是服务器在响应HTTP请求时返回的一组键值对,提供有关响应和服务器的更多信息。

  1. Status Line (状态行): 包含HTTP版本、状态码和状态描述。
  2. Server: 指定处理请求的服务器软件信息。
  3. Content-Type: 指定响应体的MIME类型。
  4. Content-Length: 指定响应体的字节长度。 

3.2 HTTP 的应答状态

        HTTP 响应状态码(HTTP Response Status Codes)是服务器在响应HTTP请求时返回的,用于指示请求的处理结果。

面试题:

  1. 一百开头是 信息
  2. 二百开头是 成功 200 OK
  3. 三百开头是 重定向
  4. 四百开头是 客户端错误 404 Not Found资源没找到 403 Forbidden
  5. 五百开头是 服务器错误

客户端的错误是:客户的请求错误,服务器无法响应。服务器的错误是:客户的请求没错误,服务器因为自身原因,现在没法提供服务。

四、Web 服务器的 C 语言实现(了解)

        Web 服务器对应的文件是 MyHttp.c,代码示例如下, 其中使用到的页面文件(.html)需要 用户自己提供,并且和程序在同一个位置:

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

#define DATALENGTH 1024 // 缓冲区大小
#define PATHLENGTH 128 // 文件路径长度

// 发送 HTTP 应答报头
void SendHeadData(int c, int size, int flag)
{
    char buff[DATALENGTH] = "HTTP/1.0 "; // HTTP 响应协议版本
    if (flag)
    {
        strcat(buff, "200 OK\r\n"); // 成功响应状态码
    }
    else
    {
        strcat(buff, "404 Not Found\r\n"); // 文件未找到状态码
    }

    strcat(buff, "Server: MyWeb/1.0\r\n"); // 服务器信息
    strcat(buff, "Content-Length: ");
    sprintf(buff + strlen(buff), "%d", size); // 响应内容长度
    strcat(buff, "\r\n");
    strcat(buff, "Content-Type: text/html;charset=utf-8\r\n"); // 响应内容类型
    strcat(buff, "\r\n");

    send(c, buff, strlen(buff), 0); // 发送响应头
}

// 发送页面文件的内容
void SendFileData(int c, int fd)
{
    while (1)
    {
        char buff[DATALENGTH] = { 0 };
        ssize_t n = read(fd, buff, DATALENGTH - 1); // 读取文件内容
        if (n <= 0)
        {
            break;
        }

        send(c, buff, (size_t)n, 0); // 发送文件内容
    }
}

// 处理客户端的请求数据
void DealClientData(int c)
{
    char requestBuff[DATALENGTH] = { 0 };
    int n = recv(c, requestBuff, DATALENGTH - 1, 0); // 接收客户端请求
    if (n <= 0)
    {
        return;
    }

    char *file = strtok(requestBuff, " ");
    file = strtok(NULL, " ");

    int flag = 1;
    char path[PATHLENGTH] = "."; // 程序所在位置
    strcat(path, file); // 构建文件路径
    int fd = open(path, O_RDONLY); // 打开文件
    if (fd == -1)
    {
        fd = open("./404.html", O_RDONLY); // 若文件未找到,则打开 404 页面
        flag = 0;
    }

    struct stat st;
    fstat(fd, &st); // 获取文件信息

    SendHeadData(c, st.st_size, flag); // 发送响应头
    SendFileData(c, fd); // 发送文件内容
    close(fd); // 关闭文件描述符
}

// 初始化服务器的套接字
int InitSocket()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字
    if(sockfd == -1) return -1;

    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(80); // 端口号设为 80
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP 地址设为本地回环地址

    int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 绑定地址
    if(res == -1) return -1;

    res = listen(sockfd, 5); // 开始监听连接请求
    if(res == -1) return -1;

    return sockfd;
}

int main()
{
    int sockfd = InitSocket();
    assert(sockfd != -1);
    while (1)
    {
        struct sockaddr_in caddr;
        socklen_t len = sizeof(caddr);
        int c = accept(sockfd, (struct sockaddr*)&caddr, &len); // 接受客户端连接
        if (c < 0)
        {
            continue;
        }

        DealClientData(c); // 处理客户端请求
        close(c); // 关闭连接
    }

    exit(0);
}




       上述代码实现了一个简单的Web服务器。它能够接收客户端的HTTP请求,并根据请求内容返回相应的文件内容或错误页面。服务器通过套接字监听端口80,接受客户端连接请求。当接收到请求后,根据请求内容打开对应的文件,并将文件内容作为HTTP响应返回给客户端。如果请求的文件不存在,则返回404页面。

至此,已经讲解完毕!篇幅较长,慢慢消化,以上就是全部内容!请务必掌握,创作不易,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见!

  • 15
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未来可期,静待花开~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值