day38——HTTP服务器

一、何为HTTP

1> Hyper Text Transfer Protocol(超文本传输协议),用于万维网(WWW:world wide web)进行超文本(html文档)信息的传送协议

2> 该协议属于应用层协议,传输层是使用TCP实现的

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

4> 实现模型

二、超文本文档HTML

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

2> 案例文档

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>欢迎来到我的主页</title>
</head>
    
<body align="center">
    <h1 >欢迎欢迎来的我的主页</h1>
    姓名:<input type="text"> 
    年龄:<input type="password"><br><br>
    留言 <textarea name="" id=""></textarea><br>
    <input type="button" value="提交">
    <input type="button" value="取消">
</body>


</html>

补充的函数

       #include <string.h>

       char *strtok(char *str, const char *delim);
    功能:通过指定的字符串,将前面的字符串进行分割
    参数1:要被分割的字符串
    参数2:分隔符
    返回值:成功返回分割出来的第一个字符串的起始地址,后续如果想要后面的字符串,参数1填NULL即可
    
    
           #include <unistd.h>

       int access(const char *pathname, int mode);
        功能:判断给定的文件是否具有某个功能
        参数1:要判断的文件
        参数2:要判断的权限
                R_OK:读权限
                W_OK:写权限
                X_OK:可执行权限
                F_OK:是否存在
        返回值:如果要判断的权限全都存在,则返回0,否则返回-1并置位错误码

三、客户端请求

3.1 请求首部

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

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

3> 方法:

GET:表示发送请求数据(常用)

POST:应答数据(常用)

DELETE:删除文档

PUT:上传文件

4> 需要对发来的请求进行解析,需要获取请求头部部分的方法以及url以便于相应

#include<myhead.h>
#define SER_PORT 80          //服务器端口号
#define SER_IP "192.168.0.133"    //服务器ip地址

int get_one_line(int newfd, char msg[])
{
    char buf = '\0';         //读取消息
    int i = 0;             //填充数组

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

    msg[i] = '\0';

}



int main(int argc, const char *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

    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //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 定义变量用于接收客户端的信息
    struct sockaddr_in cin;          //用于接收地址信息
    socklen_t addrlen = sizeof(cin);  //用于接收长度

    
    while(1)
    {

        //4.2 接收连接
        int newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen) ;
        if(newfd == -1)
        {
            perror("accept error");
            return -1;
        }
        printf("[%s:%d]: 已成功连接一个客户端\n", \
                inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));

        //5、数据收发
        char buf[128] = "";

        //接收请求头部
        get_one_line(newfd, buf);           //获取到请求行
        //printf("buf = %s\n", buf);
        
        //获取请求请求方法
        char mathod[5] = "";
        strcpy(mathod, strtok(buf, " "));     //将分割下的第一个串放入到mathod中
        char url[50] = "";
        strcpy(url, strtok(NULL, " "));
        //printf("mathod = %s, url = %s\n", mathod, url);
        //判断是否为GET方法
        if(strcmp(mathod, "GET") == 0)
        {
            //说明要进行get请求,完成响应工作
            //重组url
            snprintf(url, "./htmls%s", url);

            //判断要访问的文件是否存在
            if(access(url, F_OK) ==-1)
            {
                //返回一个响应为NOT FOND的网页
                //do_notfond(newfd);
                return -1;
            }else
            {
                //给出响应
                //do_response(newfd);

            }

        }else
        {
            //非法请求
            //do_illigal(newfd);   
        }

    }

    //6、关闭监听
    close(sfd);

    return 0;
}

3.2 响应首部

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

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

3> 常见的响应代号

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

100:表示服务器已经结束请求头,并且客户的响应发送数据请求主体出错
200:OK
201:表示请求已经成功,服务器返回了主体部分
202:表示请求已经成功,并且服务器上创建了新的资源
404:NOT FOUND
500:服务器内核出错
503:服务器网关出错
#include<myhead.h>
#define SER_PORT 80          //服务器端口号
#define SER_IP "192.168.0.172"    //服务器ip地址

int get_one_line(int newfd, char msg[])
{
    char buf = '\0';         //读取消息
    int i = 0;             //填充数组

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

    msg[i] = '\0';

}


//定义发送头部函数
int send_head(int newfd, int file_size)
{
    char *head = "HTTP/1.1 200 OK\r\n\
                  Content-Type:text/html\r\n";
    char new_head[512] = "";
    //将要发送的数据长度连接
    snprintf(new_head, sizeof(new_head), "%sContent-Length:%d\r\n\r\n", head, file_size);

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

    //将头部发送给客户端
    int res = send(newfd, new_head, strlen(new_head), 0);

    return res;
}

//定义发送文件的函数
void send_html(int newfd, int fd)
{
    //定义搬运工
    char buf[128] = "";

    while(1)
    {
        //从文件中读取数据
        int res = read(fd, buf, sizeof(buf));
        if(res == -1)
        {
            //服务器内部错误
            //do_server_error(newfd);
            return ;
        }else if(res == 0)
        {
            break;
        }

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

//定义服务器内部出差函数
void do_server_error(int newfd)
{
    char *msg = "HTTP/1.1 502 SERVER_ERROR\r\n\
                 Content-Type:text/html\r\n\r\n";

    //发送给客户端
    send(newfd, msg, strlen(msg), 0);

    //定义要发送的数据
    char *m = "<!DOCTYPE html>\n\
<html lang=\"en\"> \n\ 
<head>\n\                  
    <meta charset=\"UTF-8\" />\n\
    <title>Document</title>\n\
</head>\n\
<body align=\"center\">\n\ 
    <h3>服务器内部出错</h3>\n\
    <p>文件描述符打开出错</p>\n\
</body>\n\
</html>";

    //发送给服务器
    send(newfd, m, strlen(m), 0);
}





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

    //2、获取文件的长度
    int file_size = lseek(fd, 0, SEEK_END);
    
    //3、封装响应头部发送给客户端
    int res = send_head(newfd, file_size);
    
    if(res != 0)
    {
        //4、发送html文档给客户端
        lseek(fd, 0, SEEK_SET);
        send_html(newfd, fd);
    }

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

//定义notfount函数
void do_notfond(int newfd)
{
    char *msg = "HTTP/1.1 404 NOT FOUND\r\n\
                 Content-Type:text/html\r\n\r\n\
                 <!DOCTYPE html>\n\
<html lang=\"en\"> \n\
<head>\n\ 
    <meta charset=\"UTF-8\" />\n\
    <title>Document</title>\n\
</head>\n\
<body align=\"center\">\n\ 
    <h3>404  NOT FOUND</h3>\n\
    <p>您要访问的网页不存在</p>\n\
</body>\n\
</html>";

    //发送给服务器
    send(newfd, msg, strlen(msg), 0);

}






/***************************************主程序***********************************/
int main(int argc, const char *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

    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //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 定义变量用于接收客户端的信息
    struct sockaddr_in cin;          //用于接收地址信息
    socklen_t addrlen = sizeof(cin);  //用于接收长度

    
    while(1)
    {

        //4.2 接收连接
        int newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen) ;
        if(newfd == -1)
        {
            perror("accept error");
            return -1;
        }
        printf("[%s:%d]: 已成功连接一个客户端\n", \
                inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));





        //5、数据收发
        char buf[128] = "";

        //接收请求头部
        get_one_line(newfd, buf);           //获取到请求行
        //printf("buf = %s\n", buf);
        
        //获取请求请求方法
        char mathod[5] = "";
        strcpy(mathod, strtok(buf, " "));     //将分割下的第一个串放入到mathod中
        char url[50] = "";
        char new_url[50] = "";
        strcpy(url, strtok(NULL, " "));
        //printf("mathod = %s, url = %s\n", mathod, url);
        //判断是否为GET方法
        if(strcmp(mathod, "GET") == 0)
        {
            //说明要进行get请求,完成响应工作
            //重组url
            snprintf(new_url, sizeof(new_url), "./htmls%s", url);
            printf("url = %s\n", new_url);

            //判断要访问的文件是否存在
            if(access(new_url, F_OK) ==-1)
            {
                //返回一个响应为NOT FOND的网页
                do_notfond(newfd);
                
            }else
            {
                //给出响应
                do_response(newfd, new_url);
            }

        }else
        {
            //非法请求
            //do_illigal(newfd);   
        }

    }

    //6、关闭监听
    close(sfd);

    return 0;
}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值