从零开始学写HTTP服务器(三)请求资源

客户端通过URI,Content-Type等请求头的内容访问服务端的特定实体,今天主要学习这部分内容。

(一)C++文件I/O

服务端收到HTTP请求后,需要对请求头进行解析,之后获取响应资源,最后返回。
本文使用C++ file stream的方式读取文件后返回。

C++ file stream提供3个接口:

1.ofstream: Stream class to write on files
2.ifstream: Stream class to read from files
3.fstream: Stream class to both read and write from/to files.

1.1 ofstream

将内容输出到文件,用法跟标准的I/O函数类似:

#include 
#include 
using namespace std;

int main () {
  ofstream myfile;
  myfile.open ("example.txt");
  if(myfile.is_open())
  {
      myfile << "Writing this to a file.\n";
      myfile.close();
  }
  return 0;
}

执行之后,当前目录的example.txt文件中将有”Writing this to a file.”输出。

1.2 ifstream

从文件输入内容到程序,跟标准I/O函数用法类似。

#include 
#include 
#include 
using namespace std;
int main()
{
    string content;
    string line;
    ifstream myfile ("index.html");
    if(myfile.is_open())
    {
        while(getline(myfile,line))
        {
            content+=line;
        }
        myfile.close();
    }
    cout<< content<return 0;
}

(二)获取uri

我们直到HTTP请求第一行是请求行,接下来是请求头,再接下来是请求报文。
一个请求行示例如下:

GET /index.html HTTP/1.1<CR><LF>

分别是请求方法(GET)、请求URI(根目录下的index.html)、HTTP协议版本(HTTP/1.1)。
分别是\r\n 换行和回车。
使用string流的方式对请求进行解析:

stringstream ss;
ss<string method;
ss>>method;
string uri;
ss>>uri;
string version;
ss>>version;

(三)mime-type

互联网媒体类型(Internet media type,也称为MIME类型(MIME type)或内容类型(content type))是给互联网上传输的内容赋予的分类类型。一份内容的互联网媒体类型是由其文件格式与内容决定的。互联网媒体类型与文件拓展名相对应,因此计算机系统常常通过拓展名来确定一个文件的媒体类型并决定与其相关联的软件。互联网媒体类型的分类标准由互联网号码分配局(IANA)发布。1996年十一月,媒体类型在RFC 2045中被最初定义,当时仅被使用在SMTP协议的电子邮件中。现在其他的协议(比如HTTP或者SIP)也都常使用MIME类型。 一个MIME类型至少包括两个部分:一个类型(type)和一个子类型(subtype)。此外,它还可能包括一个或多个可选参数(optional parameter)。比如,HTML文件的互联网媒体类型可能是

text/html; charset = UTF-8

在这个例子中,文件类型为text,子类型为html,而charset是一个可选参数,其值为UTF-8。

例如在nginx的安装目录下的conf文件夹下有mime.types将扩展名和媒体类型对应,这样就可以通过不同的文件扩展名去规定响应的Content-Type

types {
    text/html                             html htm shtml;
    text/css                              css;
    text/xml                              xml;
    image/gif                             gif;
    image/jpeg                            jpeg jpg;
    application/x-javascript              js;
    application/atom+xml                  atom;
    application/rss+xml                   rss;

    text/mathml                           mml;
    text/plain                            txt;
    text/vnd.sun.j2me.app-descriptor      jad;
    text/vnd.wap.wml                      wml;
    text/x-component                      htc;

    image/png                             png;
    image/tiff                            tif tiff;
    image/vnd.wap.wbmp                    wbmp;
    image/x-icon                          ico;
    image/x-jng                           jng;
    image/x-ms-bmp                        bmp;
    image/svg+xml                         svg svgz;
    image/webp                            webp;

    application/java-archive              jar war ear;
    application/mac-binhex40              hqx;
    application/msword                    doc;
    application/pdf                       pdf;
    application/postscript                ps eps ai;
    application/rtf                       rtf;
    application/vnd.ms-excel              xls;
    application/vnd.ms-powerpoint         ppt;
    application/vnd.wap.wmlc              wmlc;
    application/vnd.google-earth.kml+xml  kml;
    application/vnd.google-earth.kmz      kmz;
    application/x-7z-compressed           7z;
    application/x-cocoa                   cco;
    application/x-java-archive-diff       jardiff;
    application/x-java-jnlp-file          jnlp;
    application/x-makeself                run;
    application/x-perl                    pl pm;
    application/x-pilot                   prc pdb;
    application/x-rar-compressed          rar;
    application/x-redhat-package-manager  rpm;
    application/x-sea                     sea;
    application/x-shockwave-flash         swf;
    application/x-stuffit                 sit;
    application/x-tcl                     tcl tk;
    application/x-x509-ca-cert            der pem crt;
    application/x-xpinstall               xpi;
    application/xhtml+xml                 xhtml;
    application/zip                       zip;

    application/octet-stream              bin exe dll;
    application/octet-stream              deb;
    application/octet-stream              dmg;
    application/octet-stream              eot;
    application/octet-stream              iso img;
    application/octet-stream              msi msp msm;

    audio/midi                            mid midi kar;
    audio/mpeg                            mp3;
    audio/ogg                             ogg;
    audio/x-m4a                           m4a;
    audio/x-realaudio                     ra;

    video/3gpp                            3gpp 3gp;
    video/mp4                             mp4;
    video/mpeg                            mpeg mpg;
    video/quicktime                       mov;
    video/webm                            webm;
    video/x-flv                           flv;
    video/x-m4v                           m4v;
    video/x-mng                           mng;
    video/x-ms-asf                        asx asf;
    video/x-ms-wmv                        wmv;
    video/x-msvideo                       avi;
}

真正使用时需要从mime.types文件中找到对应文件扩展匹配构造相应的响应头。我为了简答期间这一步骤就省去了。。。


(四)简单示例

在从零开始学写HTTP服务器(二)socket编程实现简单的http server的基础上添加了文件IO。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
#define BUFFER_SIZE 512

int Socket(int ,int,int);
void Bind(int ,const struct sockaddr*sa,socklen_t salen);
void Listen(int ,int);
int Accept(int,struct sockaddr*,socklen_t*);
void handleAccept(int);
void handleHttp(int);
int getRequest(int);


int main(int argc,char **argv)
{
    const int port = 1024;//listen port
    int listenfd=Socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in serverAddr;
    serverAddr.sin_family=AF_INET;
    serverAddr.sin_addr.s_addr=INADDR_ANY;
    serverAddr.sin_port=htons(port);
    Bind(listenfd,(struct sockaddr*)&serverAddr,sizeof(serverAddr));
    Listen(listenfd,5);

    while(true)
    {
        handleAccept(listenfd);
    }

}

int Socket(int family , int type,int protocol)
{
    int     n;
    if ( (n = socket(family, type, protocol)) < 0)
    {
        printf("socket error\r\n");
        return -1;
    }
    return(n);

}
void
Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if (bind(fd, sa, salen) < 0)
    {
        printf("bind error\r\n");
        exit(-1);
    }
}
void
Listen(int fd, int backlog)
{
    char    *ptr;

        /*4can override 2nd argument with environment variable */
    if ( (ptr = getenv("LISTENQ")) != NULL)
        backlog = atoi(ptr);

    if (listen(fd, backlog) < 0)
    {
        printf("listen error\r\n");
        return ;
    }
}
int
Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
    int     n;

again:
    if ( (n = accept(fd, sa, salenptr)) < 0) {
#ifdef  EPROTO
        if (errno == EPROTO || errno == ECONNABORTED)
#else
        if (errno == ECONNABORTED)
#endif
            goto again;
        else
        {
            printf("accept error\r\n");
            return -1;
        }
    }
    return(n);
}

void handleAccept(int listenfd)
{
    sockaddr_in clientAddr;
    socklen_t clientLen=sizeof(clientAddr);
    int connfd=Accept(listenfd,(sockaddr *)&clientAddr,&clientLen);
    handleHttp(connfd);
    close(connfd);
}

void handleHttp(int connfd)
{
    if(getRequest(connfd)<0)
    {
        perror("http request get error");
        exit(-1);
    }
}
int getRequest(int socket)
{
    int msgLen=0;
    char buffer[BUFFER_SIZE];
    memset (buffer,'\0', BUFFER_SIZE);
    if ((msgLen = recv(socket, buffer, BUFFER_SIZE, 0)) == -1)
    {
        printf("Error handling incoming request");
        return -1;
    }
    stringstream ss;
    ss<string method;
    ss>>method;
    string uri;
    ss>>uri;
    string version;
    ss>>version;
    //
    uri.erase(uri.begin());
    ifstream myfile(uri);
    string line;
    string content;
    if(myfile.is_open())
    {
        while(getline(myfile,line))
        {
            content+=line;
        }
        myfile.close();
    }

    string statusCode("200 OK");
    string contentType("text/html");
    //string content("my simple httpserver

hello zx world

");
string contentSize(std::to_string(content.size())); string head("\r\nHTTP/1.1 "); string ContentType("\r\nContent-Type: "); string ServerHead("\r\nServer: localhost"); string ContentLength("\r\nContent-Length: "); string Date("\r\nDate: "); string Newline("\r\n"); time_t rawtime; time(&rawtime); string message; message+=head; message+=statusCode; message+=ContentType; message+=contentType; message+=ServerHead; message+=ContentLength; message+=contentSize; message+=Date; message+=(string)ctime(&rawtime); message+=Newline; int messageLength=message.size(); int n; n=send(socket,message.c_str(),messageLength,0); n=send(socket,content.c_str(),content.size(),0); return n; }

直接拷贝了nginx的index.html到当前目录,编译运行,分别用curl和chrome 测试

curl -v localhost:1024/index.html

从零开始学写HTTP服务器(三)请求资源_第1张图片

chrome测试:
从零开始学写HTTP服务器(三)请求资源_第2张图片

从零开始学写HTTP服务器(三)请求资源_第3张图片

(五)参考

1.https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%94%E7%BD%91%E5%AA%92%E4%BD%93%E7%B1%BB%E5%9E%8B
2.https://github.com/fekberg/GoHttp
3.http://www.cplusplus.com/doc/tutorial/files/

你可能感兴趣的:(Http协议)