浏览器请求服务器静态文件的实现

服务器是基于epoll实现 ET模式 非阻塞模式,浏览器用Get请求方式访问服务器本地资源

写这个demo过程中遇到问题:

          1.浏览器 访问的 例如  192.168.1.130:8989/ 这个 '/'这个根文件不是服务器跟目录而是服务器的本地资源相对路径,所有要切换服务器的目录到 浏览器资源访问的目录下 通过chdir设置,浏览器访的 路径  假设一个图片 192.168.1.130:8989/wsy.png; 通过http数据的解析 我们得到的是 

/wsy.png 此时需要去掉 '/' 

          2.epoll ET模型 ,收到新的客户端连接,需要将通信描述符标记 改为非阻塞模式,事件加上ETPOLLET 此时 read 和recv 不阻塞,通过while循环将数据读完, 

          3.服务器给客户端发送比较大的图片时候的由于服务器发送的数据过快,导致浏览器显示数据不全,(由于缓冲机制覆盖问题 丢失数据导师浏览器的数据缺陷注:tcp协议数据是不会跌势,是浏览器的本身刷新的原因),所以需要加发送数据给浏览器的时候 稍微延时处理即可

          4.浏览器访问多个文件夹里面的数据会出现 超链接访问的路径不对,

 if ( S_ISDIR(st.st_mode))  //the file is dir , so add '/'
        {
            sprintf(buf+strlen(buf), "<tr><td><a charset=\"UTF-8\" href=\"%s
/\">%s</a></td><td>%s</td></tr>",name, name, subBuf); 需要判断是不是一个文件 然后在超链接后面加一个'/'
        },这样就可以解决路径访问不对的问题

        5.由于http数据的传输过程中不会有中文的,所以浏览器带有中文的数据  比如:192.168.1.130:8989/风景.png, 此时浏览器会自动将 ‘风景’转化为 Unicode 码形式 %E8%86%84.png,此时浏览器访问服务器本地资源就找不到,我们就要将浏览器中文编码unicode还原为utf-8格式编码,将16进制转化为10进制字符,函数 decodeMsg(),就可以找到服务器本地资源目录的风景.png图片资源

 

 

// server.c
#ifndef _SERVER_H_

#define _SERVER_H_



#include<stdlib.h>

#include<stdio.h>

#include<sys/epoll.h>

#include<arpa/inet.h>

#include<unistd.h>

#include<errno.h>

#include<fcntl.h>

#include<string.h>

#include<sys/stat.h>

#include<strings.h>

#include<dirent.h>

#include<ctype.h>



int initListenFd(unsigned short prot);



int epollRun(int lfd);



int acceptNewConnect(int lfd, int epfd);



int recvHttpRequest(int fd, int epfd);



int parseHttpLine(const char* buf, int fd);



int sendHttpHeadMsg(int fd, int staus, const char* dscr, const char* type, int leght);



//

int  sendFileInfo(int fd, const char* name);



int sendDirInfo(int fd, const char*dirname);



//

const char* getFileTypeByName(const char* name);



void decodeMsg(char* to, char* from);



int hextoDex(char c);









#endif



//server.c
#include "server.h"

int initListenFd(unsigned short prot)
{
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1)
    {
        perror("socket");
        return -1;
    }

    int opt = 1;  //set lfd no_block
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(prot);
    addr.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
    if (-1 == ret)
    {
        perror("bind");
        return -1;
    }

    ret = listen(lfd, 128);
    if (-1 == ret)
    {
        perror("listen");
        return -1;
    }
    
    return lfd;
}

int epollRun(int lfd)
{
    int epfd = epoll_create(1);
    if (epfd < 0)
    {
        perror("epfd");
        exit(0);
        /* code */
    }

    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = lfd;
    int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
    if (ret == -1)
    {
        perror("epoll_ctl");
        exit(0);
        /* code */
    }

    struct epoll_event evs[1024];
    int size = sizeof(evs) / sizeof(evs[0]);
   

    int falg = 0;
    while (1)
    {
        if (falg)
        {
            break;
        }
        
        printf("epoll_wait..........\n");
        int num = epoll_wait(epfd, evs, size, -1);
        if (-1 == num)
        {
            perror("epoll_wait");
            exit(0);
            /* code */
        }
        for (size_t i = 0; i < num; i++)
        {
            int curfd = evs[i].data.fd;

            if (evs[i].events & EPOLLOUT)
            {
                continue;
            }
            if (curfd == lfd)
            {
                // create new client connetct
                int ret = acceptNewConnect(lfd, epfd);
                if (ret == -1)
                {
                    break;
                }
                
            }
            else
            {   
                recvHttpRequest(curfd, epfd);
            }
            
            
            
        }
        
        
       
    }
    
}

int acceptNewConnect(int lfd, int epfd)
{
    //new client coming 
    struct sockaddr_in clientaddr;
    int clen = sizeof(clientaddr);
    // memset(&clientaddr, )
    int cfd = accept(lfd, (struct sockaddr*)&clientaddr, &clen);
    if (cfd == -1)
    {
        perror("accept");
        return -1;
        /* code */
    }
    char myip[28];
    memset(myip, 0 ,sizeof(myip));
    printf("new client accept ip=%s, prot=%d\n", inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr,
    myip, sizeof(myip)), ntohs(clientaddr.sin_port));

    //set  fd  non_block
    int flag = fcntl(cfd, F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(cfd, F_SETFL, flag);

    //deaulf  ET
    struct epoll_event ev;   
    ev.events = EPOLLIN | EPOLLET; //set ET Model
    ev.data.fd = cfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);
    return 0;
}

int recvHttpRequest(int fd, int epfd)
{
    // set fd o_nonblock   ET mode
    char buf[4096];
    char temp[1024];
    int  rlen, tolenLen = 0;
    while ( (rlen = recv(fd, temp, sizeof(temp), 0)) > 0 )
    {   
        if ( tolenLen + rlen <= sizeof(buf) )
        {
            memcpy(buf + tolenLen, temp, rlen);
            tolenLen += rlen;
        }
        else
        {
            break;
        }
    }

    /*
        GET /cat.gif HTTP/1.1   \r\n
        Host: 192.168.32.130:8989  \r\n
        Connection: keep-alive      \r\n
        Cache-Control: max-age=0     \r\n
        Upgrade-Insecure-Requests: 1     \r\n
        User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36
        Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,;q=0.8,application/signed-exchange;v=b3;q=0.9
        Accept-Encoding: gzip, deflate
        Accept-Language: zh-CN,zh;q=0.9
    */

    printf("recv http data:\n %s\n", buf);
    if (rlen == -1 && errno == EAGAIN)  
    {
        printf("read data done\n");
        //find "\r\n" pointer
        //get lien
        //  GET /cat.gif HTTP/1.1   \r\n
        char* p = strstr(buf, "\r\n");
        if (p)
        {
            int len = p - buf;
            buf[len] = '\0';
        }
        parseHttpLine(buf, fd);
        
    }
    else if(rlen == 0)
    {
        printf("old client close\n");
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        close(fd);
    }
    else
    {
        perror("read");
    }

    //deal headData
    
        


    return -1;
    
}

int parseHttpLine(const char* buf, int fd)
{
    char methon[10] = {0};
    char path[1024] = {0};
    // GET /wangcheng/res http1.1
    sscanf(buf, "%[^ ] %[^ ]", methon, path);   
     if(strcasecmp(methon, "get") != 0 )
    {
        printf("this is post request\n");
        return -1;
    }

    char* file = NULL;
    //if path with chineese, so 16hex  to 10.   exp:path= ./wsy%E8%85%12.png  unicode
    printf("decodeMsg1 = %s", path);

    decodeMsg(path, path);

    printf("decodeMsg2 = %s", path);
    

    if (strcmp(path, "/") == 0 )  // is res ./
    {
        file = "./";    //  res/
    }
    else  // is filepath   
    {
        file = path + 1;  
    }

    printf("current working directory: %s\n", getcwd(NULL, 0));
    //throught path get file attrs
    struct stat st;
    int ret = stat(file, &st);
    if (ret == -1)
    {
        printf("file is not exit");
        sendHttpHeadMsg(fd, 404, "Not Found", getFileTypeByName("404.html"), -1);
        sendFileInfo(fd, "404.html");
        return -1;
    }
    
    if( S_ISDIR(st.st_mode)) //this is dir
    {
        sendHttpHeadMsg(fd, 200, "OK", getFileTypeByName(".html"), -1);
        sendDirInfo(fd, file);
    }
    else    //this is file
    {
        
         sendHttpHeadMsg(fd, 200, "OK", getFileTypeByName(file), st.st_size);
         sendFileInfo(fd, file);
    }
    
    
    
    return 0;
}

int sendHttpHeadMsg(int fd, int status, const char* dscr, const char* type, int leght)
{
    /* 
        http/1.1 200 ok  
        Content-Type:image/jpg
        Content-Length: -1

        send filedata
    */
    char buf[1024] = {0};

    sprintf(buf, "http/1.1 %d %s\r\n", status, dscr);

    sprintf(buf+strlen(buf), "Content-Type: %s\r\n", type);
    sprintf(buf+strlen(buf), "Content-Length: %d\r\n\r\n", leght);
    printf("headMsg = %s\n", buf);
    int ret = send(fd, buf, strlen(buf), 0);
    if (-1 == ret)
    {
        perror("send");
        return -1;
    }
    
    return 0;
}
//
int  sendFileInfo(int cfd, const char* name)
{
    //open file
    int fd = open(name, O_RDONLY);
    while (1)
    {
        char buf[1024] = {0};
        int len = read(fd, buf, sizeof(buf));
        if (len > 0)
        {
            // printf("read res data =%s\n", buf);
            send(cfd, buf, len, 0);

            //because of send so fast , client accept data problem so, add sleep
            usleep(100);
        }
        else if( len == 0)  
        {
            printf("read file data done\n");
            close(fd);
            break;
        }
        else
        {
            perror("file read");
            return -1;
        }
        
    }
    
    return 0;
}

const char* getFileTypeByName(const char* name)
{
    char* dot = strrchr(name, '.');
    if (dot == NULL)
    {
        return "text/plain";
    }
    if ( strcasecmp(dot, ".html") == 0 )
    {
        return "text/html";
    }
    else if( strcasecmp(dot, ".img") == 0 )
    {
        return "application/x-img";
    }
    else if( strcasecmp(dot, ".jpg") == 0 )
    {
        return "image/jpeg";
    }
    else if( strcasecmp(dot, ".sor") == 0 )
    {
        return "text/plain";
    }
    else if( strcasecmp(dot, ".gif") == 0 )
    {
        return "image/gif";
    }
    else if( strcasecmp(dot, ".txt") == 0 )
    {
        return "text/plain";
    }
    else{
        return "text/plain";
    }
    
}

int sendDirInfo(int fd, const char*dirname)
{
    /*
        <html>
            <head>
                <title>this is dir</title>
            </head>
            <body>
                <table>
                    <tr>
                        <td>文件名字</td>
                        <td>文件大小</td>
                        <td>文件时间</td>
                    </tr>
                    <tr>
                        <td>文件名字</td>
                        <td>文件大小</td>
                        <td>文件时间</td>
                    </tr>
                </table>
            </body>
        </html>
    send html info to client 
    */

    

    struct dirent**  namelist;   // struct dirent* p[size]; malloc memory
    //for all file 
    int num = scandir(dirname, &namelist, NULL, alphasort);
    char buf[4096] ={0};
    // <meta http-equiv="Content-Type"content="text/html;charset=utf-8">  del luan ma
    sprintf(buf, "<html> <meta http-equiv=\"Content-Type\"content=\"text/html;charset=utf-8\"><head><title>你好! %s</title></head><body><table><tr><td>FileName</td><td>FileSize</td></tr>", dirname);
    char subBuf[1024]={0};
    struct stat st;

    for (size_t i = 0; i < num; i++)
    {
        char* name = namelist[i]->d_name;
        sprintf(subBuf, "%s%s", dirname, name);
        int ret = stat(subBuf, &st);
        memset(subBuf, 0, sizeof(subBuf));
        short size = st.st_size * 1.0;
        if (size > 1024)
        {
            sprintf(subBuf, "%ldKB", size / 1024);
        }
        else if(size > 1024 * 1024)
        {
            sprintf(subBuf, "%ldMB", size / (1024*1024) );
        }
        else
        {
            sprintf(subBuf, "%ld", size);

        }

        if ( S_ISDIR(st.st_mode))  //the file is dir , so add '/'
        {
            sprintf(buf+strlen(buf), "<tr><td><a charset=\"UTF-8\" href=\"%s/\">%s</a></td><td>%s</td></tr>",name, name, subBuf);
        }
        else{

            sprintf(buf+strlen(buf), "<tr><td><a charset=\"UTF-8\" href=\"%s\">%s</a></td><td>%s</td></tr>",name, name, subBuf);

        }
        
        
        
        send(fd, buf, strlen(buf), 0);

        memset(buf, 0, sizeof(buf));
        free(namelist[i]);
    }
    sprintf(buf, "</table></body></html>");
    send(fd, buf, strlen(buf), 0);

    free(namelist);
    
    return 0;
}

void decodeMsg(char* to, char* from)
{
    for ( ; *from != '\0'; ++to, ++from)
    {
        //isxdigit  check char  is  hex ?
        if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2]))
        {
            *to = hextoDex(from[1]) * 16 + hextoDex(from[2]);
            from += 2;
        }
        else
        {
            *to = *from;
        }
        
    }
    *to = '\0';

}

int hextoDex(char c)
{
    if (c >= '0' && c <= '9')
    {
        return c - '0';
    }
    else if ( c >= 'a' && c <= 'f')
    {
        return c - 'a' + 10;
    }
    else if ( c >= 'A' && c <= 'F')
    {
        return c - 'A' + 10;
    }
}



//main.c
#include"server.h"

int main(int agrc, char* agrv[])
{
    if (agrc < 3)
    {
        printf("please input ./a.out /wangcheng/res 8989\n");
        return -1;
        /* code */
    }

    //get port
    unsigned short port = atoi(agrv[2]);

    //change dir path cmd  curdir invironment
    char* path = agrv[1];
    printf("main path = %s\n", path);
    chdir(path);
    
    int lfd = initListenFd(port);

    epollRun(lfd);    
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

时间溜走了

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

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

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

打赏作者

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

抵扣说明:

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

余额充值