0
点赞
收藏
分享

微信扫一扫

C#基础-五大数据类型

南陵王梁枫 04-03 23:00 阅读 3

使用C语言实现Linux下的并发Http服务器

文章目录

先备知识

*html*,全称Hypertext Markup Language,也就是“超文本链接标示语言”。HTML文本是由 HTML命令组成的描述性文本,HTML 命令可以说明文字、 图形、动画、声音、表格、链接等。 即平常上网所看到的的网页。

Http协议

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

请求格式:
客户端请求

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。

在这里插入图片描述

服务端响应

服务器响应客户端的HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

请添加图片描述

内容响应代号代号描述
服务器上存在请求的内容,并可以响应给客户端200OK
客户端的请求有异常,方法有问题501Method Not Implemented
服务器收到请求后,因为自生的问题没法响应500Internal Server Error
请求的内容不存在404NOT FOUND
客户端发送的请求格式有问题等400BAD REQUEST
Demo
浏览器请求:
GET /demo.html HTTP/1.1
Host: 47.100.162.**
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6788.400 QQBrowser/10.3.2767.400
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie:cna=BT0+EoVi1FACAXH3Nv5I7h6k;isg=BIOD99I03BNYvZDfE2FJUOsMB0ftUBZcBFi4E7VgYOJZdKOWPcvRinAl6kSfVG8y


 
服务器响应:
HTTP/1.0 200 OK
Server: Martin Server
Content-Type: text/html
Connection: Close
Content-Length: 526
 ---一下为content-----
 

实现Mini的Http服务器流程

请添加图片描述

接收Http请求

实现按行读取请求头部

inline int get_line(int , char *, int );
int get_line(int sock, char *buf, int size)
{

    int count = 0;
    int len = 0;
    char ch = '\0';

    while (count < (size - 1) && ch != '\n')
    {
        len = read(sock, &ch, 1);
        if (len == 1)
        {
            if (ch == '\r')
            {
                
                continue;
            }
            else if (ch == '\n')
            {
               
                break;
            }

            buf[count] = ch;
            count++;
        }
        else if (len == -1)
        {
            perror("read faild");
            count = 0;
            break;
        }
        else
        {
            fprintf(stderr, "client close the connect \n");
            count = 0;
            break;
        }
    }

    if (count >= 0)
        buf[count] = '\0';

    return count;
}

请求头部的结束

解析请求

void * do_http_request(void * pclient_sock)
{

    int len;
    char buf[512];

    char url[256];

    char method[64];
    int client_sock = *(int *)pclient_sock;
    len =  get_line(client_sock, buf, sizeof(buf));

    // 读取客户端的请求

    // 读取请求行

   
    // printf("%s\n", buf);
    if (buf == '\0')
    {
        if (debug)
        {
            printf("No request line ,The buff is null\n");
        }
    }
    if (debug)
        printf("The len is %d\n", len);
    if (len > 0)
    {
        int i = 0, j = 0;

        while (!isspace(buf[j]) && (i < (sizeof(method) - 1)))
        {
            method[i] = buf[j];
            i++;
            j++;
        }
        method[i] = '\0';
        // printf("the method is \t:%s\n", method);
        if (strncasecmp(method, "GET", i) == 0)
        {
            // 处理GET请求
            if (debug)

                printf("method = GET\n");
            while (isspace(buf[j++])) // 跳过白空格

                i = 0;
            while (!isspace(buf[j]) && (i < (sizeof(url) - 1)))
            {

                url[i] = buf[j];
                i++;
                j++;
            }

            url[i] = '\0';

            if (debug)
                printf("The url is\t:%s\n", url);

            do
            {
                len = get_line(client_sock, buf, sizeof(buf));
                if (debug)
                    printf("read: %s\n", buf);
                /* code */
            } while (len > 0);

            /* 定位本地服务器的html文件*/

            //

            {
                char *pos = strchr(url, '?');
                if (pos)
                {
                    *pos = '\0';
                    printf("real url: %s\n", url);
                }
            }

            // 读取本地文件
            char path[512];
            sprintf(path, "./html_docs/%s", url);
            if (debug)
                printf("path is %s\n", path);

            struct stat st;
            // 读取文件

            if (stat(path, &st) == -1)
            {
                fprintf(stderr, "stat %s failed,reson :%s \n", path, strerror(errno));

                fprintf(stderr, "warning! file not found\n");
                // 返回404
                not_found(client_sock);
            }
            else
            {
                // 文件存在
                if (S_ISDIR(st.st_mode))
                {

                    strcat(path, "/index.html");
                    // 返回目录
                    fprintf(stderr, "warning! file is a dir\n");
                    // 返回404
                }
                do_http_response(client_sock, path);
            }
        }
        else
        {
            fprintf(stderr, "warning! other request[%s]\n", method);
            // 处理其他请求
            if (debug)
                printf("method != GET\n");

            // read http header ,and send error code to the client
            // 响应客户都 501 the method is not defined
            do
            {
                len = get_line(client_sock, buf, sizeof(buf));
                if (debug)
                    printf("read: %s\n", buf);

            } while (len > 0);

            // unimplemented(client_sock);//在响应时实现
        }

        // 读取Http的头部
    }
    else
    {
        // request error
        bad_request(client_sock);
    }
    close(client_sock);
	if(pclient_sock) free(pclient_sock);//释放动态分配的内存
	
	return NULL;
}

响应请求

void do_http_response(int client_sock, const char *path)
{
    FILE *resource = NULL;
    resource = fopen(path, "r");
    if (resource == NULL)
    {
        not_found(client_sock);
        return;
    }
    int size = fseek(resource, 0, SEEK_END);

    // 发送http头部
    send_headers(client_sock, resource);

    // 发送http_body
    send_body(client_sock, resource);

    fclose(resource);
    return;
}

读取文件(http需要发送html下的html文件

文件概念简介

请添加图片描述

inode - “索引节点”,储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。每个inode都有一个号码,操作系统用inode号码来识别不同的文件。ls -i 查看inode 号

stat函数

****作用:****返回文件的状态信息

头文件

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

函数体

int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);

path:

​ 文件的路径

buf:

​ 传入的保存文件状态的指针,用于保存文件的状态

返回值:

​ 成功返回0,失败返回-1,设置errno

结构体

struct stat {
               dev_t     st_dev;     /* ID of device containing file */
               ino_t     st_ino;     /* inode number */
               mode_t    st_mode;    /* S_ISREG(st_mode)  是一个普通文件  S_ISDIR(st_mode)  是一个目录*/
               
               nlink_t   st_nlink;   /* number of hard links */
               uid_t     st_uid;     /* user ID of owner */
               gid_t     st_gid;     /* group ID of owner */
               dev_t     st_rdev;    /* device ID (if special file) */
               off_t     st_size;    /* total size, in bytes */
               blksize_t st_blksize; /* blocksize for filesystem I/O */
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
               time_t    st_atime;   /* time of last access */
               time_t    st_mtime;   /* time of last modification */
               time_t    st_ctime;   /* time of last status change */
           };

响应文件的头部

void send_headers(int client_sock, FILE *fp)
{
    struct stat st;
    if (fstat(fileno(fp), &st) == -1)
    {
        perror("fstat");
        return;
    }

    char buf[1024] = {0};
    strcpy(buf, "HTTP/1.0 200 OK\r\n");
    strcat(buf, "Content-Type: text/html; charset=utf-8\r\n");
    strcat(buf, "Server:Lucifer Web Server\r\n");
    strcat(buf, "Connection:close\r\n");
    char tmp[128];
    sprintf(tmp, "Content-Length: %ld\r\n", st.st_size);
    strcat(buf, tmp);
    if (debug)
    {
        fprintf(stdout, "send_body buf: %s\n", buf);
        fflush(stdout);
    }
    if (send(client_sock, buf, strlen(buf), 0) < 0)
    {
        sprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
    }
    // sendfile(client_sock, fileno(fp), NULL, st.st_size); // 发送文件内容
}

发送body

}

void send_body(int client_sock, FILE *fp)
{
    char buf[1024];

    fgets(buf, sizeof(buf), fp);

    while (!feof(fp))
    {
        int len = write(client_sock, buf, strlen(buf));

        if (len < 0)
        { // 发送body 的过程中出现问题,怎么办?1.重试? 2.
            fprintf(stderr, "send body error. reason: %s\n", strerror(errno));
            break;
        }

        if (debug)
            fprintf(stdout, "%s", buf);
        fgets(buf, sizeof(buf), fp);
    }
}

错误访问

404
void not_found(int client_sock)
{
    const char *reply = "HTTP/1.0 404 NOT FOUND\r\n\
    Content-Type: text/html\r\n\
    \r\n\
    <HTML lang=\"zh-CN\">\r\n\
    <meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\
    <HEAD>\r\n\
    <TITLE>NOT FOUND</TITLE>\r\n\
    </HEAD>\r\n\
    <BODY>\r\n\
        <P>file not find \r\n\
        <P>The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n\
    </BODY>\r\n\
    </HTML>";

    int len = write(client_sock, reply, strlen(reply));
    if (debug)
        fprintf(stdout, reply);

    if (len <= 0)
    {
        fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
    }
}
500
void iner_error(int client_sock)
{
    const char *reply = "HTTP/1.0 500 Internal Sever Error\r\n\
    Content-Type: text/html\r\n\
    \r\n\
    <HTML>\
    <HEAD>\
    <TITLE>Method Not Implemented</TITLE>\
    </HEAD>\
    <BODY>\
        <P>Error prohibited CGI execution.\
    </BODY>\
    </HTML>";

    int len = write(client_sock, reply, strlen(reply));
    if (debug)
        fprintf(stdout, reply);

    if (len <= 0)
    {
        fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
    }
}

400
void bad_request(int client_sock)
{
    // 400
    const char *reply = "HTTP/1.0 400 Bad Request\r\n\
    Content-Type: text/html\r\n\
    \r\n\
    <HTML>\
    <HEAD>\
    <TITLE>Bad Request</TITLE>\
    </HEAD>\
    <BODY>\
        <P>Error prohibited CGI execution.\
    </BODY>\
    </HTML>\r\n";
    int len = send(client_sock, reply, strlen(reply), 0);
    if (len < 0)
    {
        perror("send");
    }
    if (debug)
        printf("send bad request\n");
    return;
}
501
void unimplemented(int client_sock)
{

    const char *reply = "HTTP/1.0 501 Not Implemented\r\n\
    Content-Type: text/html\r\n\
    \r\n\
    <HTML>\
    <HEAD>\
    <TITLE>Not Implemented</TITLE>\
    </HEAD>\
    <BODY>\
        <P>Error prohibited CGI execution.\
    </BODY>\
    </HTML>";

    int len = write(client_sock, reply, strlen(reply));
    if (debug)
        fprintf(stdout, reply);

    if (len <= 0)
    {
        fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));
    }
}

一些待实现的函数

请添加图片描述

所需头文件

请添加图片描述

完整代码与讨论

Q:723550115

举报

相关推荐

0 条评论