学习记录——day33 HTTP

目录

一、HTTP相关概念

二、客服端请求

1、请求首部

[2、 响应首部](#2、 响应首部)

三、线程实现HTTP并发服务器


一、HTTP相关概念

1、HTTP,全称Hyper Text Transfer Protocol,用于万维网(world wide web)进行超文本学习的传输协议

2、HTTP属于应用层,对应的传输层是TCP

3、http是基于BS模型,即浏览器服务器模型,主要完成的是客户端请求端和服务器相应端

4、Hypertext Markup Language 超文本标记语言,属于标签式语言,能够被浏览器所识别

二、客服端请求

1、请求首部

1)任何一个http请求都由三部分组成:请求首部、请求主体、请求数据

2)对于客户端请求协议包而言,一般没有请求数据,具体格式如下

2、 响应首部

1)响应首部也是由三部分组成,分别是响应头、响应主体、响应数据

2)对于响应首部而言,必须由响应数据

3)http的响应代号:1XX (信息状态错误) 2XX(成功) 、3XX(重定向状态码)、4XX(客户端错误)、5XX(服务器出错)

三、线程实现HTTP并发服务器

有BUG

#include <myhead.h>
#define SER_PORT 80
#define SER_IP "192.168.0.106"

void do_notfound(int newfd);
void do_response(int newsfd, const char *url);
int send_head(int newfd, int file_size);
void send_html(int newfd, int fd);
void do_server_error(int newfd);
int get_one_line(int newfd, char msg[]);

// 线程用信息初传输结构体
struct Info
{
    struct sockaddr_in cin;
    int newsfd;
};

// 线程处理函数
void *deal_cli_msg(void *arg)
{
    int newsfd = ((struct Info *)arg)->newsfd;
    struct sockaddr_in cin = ((struct Info *)arg)->cin;
    while (1)
    {
        // 5、数据收发
        char buf[128] = "";

        // 获取请求头部
        get_one_line(newsfd, buf);

        // 获取请求方法
        char mathod[5] = "";              // 存储请求方法
        strcpy(mathod, strtok(buf, " ")); // 分割字符串并将分割下来的第一个字符串存入mathod
        char url[50] = "";
        char n_url[256] = "";
        strcpy(url, strtok(NULL, " ")); // 在上一次分割的基础上,将第二个字符串存入url

        if (strcmp(mathod, "GET") == 0)
        {
            // 完成相应工作
            // 重组url
            snprintf(n_url, sizeof(n_url), "./htmls%s", url);

            // 判断要访问的文件是否存在
            if (access(n_url, F_OK) == -1)
            {
                // 返回错误信息
                do_notfound(newsfd);
                break;
            }
            else
            {
                // 响应
                do_response(newsfd, n_url);
            }
        }
        else
        {
            // 非法请求
            // do_illigal(newsfd);
        }
    }
    pthread_exit(EXIT_SUCCESS);
}

int get_one_line(int newfd, char msg[])
{
    char buf = '\0';
    int i = 0;

    while (1)
    {
        int res = recv(newfd, &buf, 1, 0);
        if (0 == res)
        {
            break;
        }
        else if (1 == res)
        {
            if (buf == '\n')
            {
                break;
            }
            else
            {
                msg[i] = buf;
                i++;
            }
        }
        else
        {
            perror("recv error");
            return -1;
        }
    }

    msg[i] = '\0';
}

// 响应函数
void do_response(int newsfd, const char *url)
{
    // 1、以只读的形式打开文件
    int fd = open(url, O_RDONLY);
    if (-1 == fd)
    {
        /* 服务器内部错误 */
        do_server_error(newsfd);
        return;
    }

    // 2、获取文件大的长度
    int file_size = lseek(fd, 0, SEEK_END);

    // 3、封装响应头部发送给客服端
    int res = send_head(newsfd, file_size);
    if (0 == res)
    {
        return;
    }

    // 4、发送html文档给客服端
    lseek(fd, 0, SEEK_SET);
    // send_html(newfd,fd);

    printf("响应成功\n");
    close(fd); // 关闭文件
}

// 头部发送函数
int send_head(int newfd, int file_size)
{
    char *head = "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n";

    char n_head[512] = "";
    snprintf(n_head, sizeof(n_head), "%sContent-Length:%d\r\n\r\n", head, file_size);

    printf("n_head = %s\n", n_head);

    // 将头部发送个客服端
    int res = send(newfd, n_head, strlen(n_head), 0);

    return res;
}

// 发送文件函数
void send_html(int newfd, int fd)
{
    char buf[128] = "";
    while (1)
    {
        /* 从文件中读取数据 */
        int res = read(fd, buf, sizeof(buf));
        if (-1 == res)
        {
            /* 服务器内部出错 */
            do_server_error(newfd);
            return;
        }
        else if (0 == res)
        {
            /* 读取结束 */
            break;
        }

        // 将数据发送给客服端
        send(newfd, buf, res, 0);
    }
}

// 服务器内部出错
void do_server_error(int newfd)
{
    char *msg = "HTTP/1.1 502 SERVER_ERROR\r\nContrnt-Type-text/html\r\n";

    // 向客服端发送错误信息
    send(newfd, msg, strlen(msg), 0);

    // 定义要发送的数据
    char *msg_html = "<!DOCTYPE html>\n\
<html lang=\" en \">\n\
<head>\n\
    <meta charset=\" UTF -\
                     8 \">\n\
    <meta name=\" viewport \" content=\" width = device - width,\
         initial - scale = 1.0 \">\n\
    <title>502 SERVER_ERROR</title>\n\
</head>\n\
<body>\n\
    <h1>服务器内部出错</h1>\n\
    <p>文件描述符打开出错</p>\n\
</body>\n\
</html>";

    send(newfd, msg_html, strlen(msg_html), 0);
}

// 对象访问界面不存在
void do_notfound(int newfd)
{
    char *msg = "";

    // 向客服端发送错误信息
    send(newfd, msg, strlen(msg), 0);

    // 定义要发送的数据
    char *msg_html = "HTTP/1.1 404 NOT_FOUND\r\nContrnt-Type-text/html\r\n\
                     < !DOCTYPE html >\n<html lang = \" en \">\n<head>\n < meta charset = \" UTF -8 \">\n\
    <meta name=\" viewport \" content=\" width = device - width,initial - scale = 1.0 \">\n\
    <title>502 SERVER_ERROR</title>\n\
</head>\n\
<body>\n\
    <h1>404 NOT_FOUND</h1>\n\
    <p>e界面不存在</p>\n\
</body>\n\
</html>";
}

int main(int argc, char const *argv[])
{
    // 1、创建套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    // 参数1:ipv4的网络通信
    // 参数2:TCP通信方式
    // 参数3:默认使用一个协议

    if (sfd == -1)
    {
        perror("socket error");
        return -1;
    }

    printf("socket success, sfd = %d\n", sfd); // 3

    // 2、为套接字绑定ip地址和端口号
    // 2.1 填充地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;                // 通信域
    sin.sin_port = htons(SER_PORT);          // 端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP); // ip地址

    // 2.2 绑定
    if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    // 3、将套接字设置为被动监听状态,用于接收
    if (listen(sfd, 128) == -1)
    {
        perror("listen error");
        return -1;
    }
    printf("listen success\n");

    // 4、阻塞等待客户端的连接请求
    // 4.1 定义n变量用于e接收客服端的信息
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);

    while (1) // 循环服务器
    {
        // 4.2 接收连接
        int newsfd = accept(sfd, (struct sockaddr *)&cin, &addrlen);
        if (newsfd == -1)
        {
            perror("accept error");
            return -1;
        }
        printf("[%s:%d]:accept on\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));

        struct Info cli_info = {cin,newsfd};

        // 创建分支线程用于处理客服端请求
        pthread_t tid = -1;
        if (pthread_create(&tid, NULL, deal_cli_msg, &cli_info) != 0)
        {
            /* 出错 */
            printf("tid create error");
            return -1;
        }

        //将线程设置为n分离态
        pthread_detach(tid);
    }

    close(sfd);
    return 0;
}
相关推荐
写bug的小屁孩1 小时前
websocket身份验证
开发语言·网络·c++·qt·websocket·网络协议·qt6.3
不会编程的懒洋洋1 小时前
Spring Cloud Eureka 服务注册与发现
java·笔记·后端·学习·spring·spring cloud·eureka
scc21401 小时前
spark的学习-06
javascript·学习·spark
luoganttcc1 小时前
能否推荐开源GPU供学习GPU架构
学习·开源
垂杨有暮鸦⊙_⊙2 小时前
阅读2020-2023年《国外军用无人机装备技术发展综述》笔记_技术趋势
笔记·学习·无人机
Mephisto.java2 小时前
【大数据学习 | HBASE高级】region split机制和策略
数据库·学习·hbase
Bio Coder3 小时前
学习用 Javascript、HTML、CSS 以及 Node.js 开发一个 uTools 插件,学习计划及其周期
javascript·学习·html·开发·utools
Allen zhu3 小时前
【PowerHarmony】电鸿蒙学习记录-准备工作
学习·华为·harmonyos
华清远见成都中心3 小时前
物联网学习路线来啦!
物联网·学习
hgy89693 小时前
Ekman理论回归
学习