网络编程——6、学写HTTP服务器(3)请求资源

客户端通过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 <iostream>
#include <fstream>  //这个头文件不能丢
using namespace std;

int main () {
  ofstream myfile;  //实例化,产生 myfile 对象
  myfile.open ("example.txt");   //调用ofstream类成员函数 open
  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 <iostream>
#include <fstream>    //这个头文件不能丢
#include <string>
using namespace std;
int main()
{
    string content;
    string line;
    ifstream myfile ("index.html");  //实例化对象 myfile
    if(myfile.is_open())
    {
        while(getline(myfile,line))
        {
            content+=line;
        }
        myfile.close();
    }
    cout<< content<<endl;
    return 0;
}

二、获取URI

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

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

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

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

三、mime-type

互联网媒体类型(Internet media type,也称为MIME类型(MIME type)或内容类型(content type))是给互联网上传输的内容赋予的分类类型。在《后台开发核心技术》中:

MIME信息包括:请求修饰符客户机信息可能的内容

一份内容的互联网媒体类型是由其文件格式与内容决定的。互联网媒体类型与文件拓展名相对应,因此计算机系统常常通过拓展名来确定一个文件的媒体类型并决定与其相关联的软件。互联网媒体类型的分类标准由互联网号码分配局(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服务器(2)socket编程实现简单的http server》的基础上添加了文件IO。

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sstream>
#include <iostream>
#include <string>
#include <vector>

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<<buffer;
    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("<html><head><title>my simple httpserver</title></head><h1>hello zx world</h1></body></html>");
    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

在这里插入图片描述
Firefox测试:
在这里插入图片描述

五、参考

1.https://zh.wikipedia.org/wiki/互联网媒体类型
2.https://github.com/fekberg/GoHttp
3.http://www.cplusplus.com/doc/tutorial/files/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
教程非常不错,价值280元,绝对是干货 Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输单元(MTU)/路径MTU 以太网帧格式 ICMP ARP RARP 03TCPIP基础(三) IP数据报格式 网际校验和 路由 04TCPIP基础(四) TCP特点 TCP报文格式 连接建立三次握手 连接终止四次握手 TCP如何保证可靠性 05TCPIP基础(五) 滑动窗口协议 UDP特点 UDP报文格式 Linux网络编程之socket编程篇 06socket编程(一) 什么是socket IPv4套接口地址结构 网络字节序 字节序转换函数 地址转换函数 套接字类型 07socket编程(二) TCP客户/服务器模型 回射客户/服务器 socket、bind、listen、accept、connect 08socket编程(三) SO_REUSEADDR 处理多客户连接(process-per-conection) 点对点聊天程序实现 09socket编程(四) 流协议与粘包 粘包产生的原因 粘包处理方案 readn writen 回射客户/服务器 10socket编程(五) read、write与recv、send readline实现 用readline实现回射客户/服务器 getsockname、getpeername gethostname、gethostbyname、gethostbyaddr 11socket编程(六) TCP回射客户/服务器 TCP是个流协议 僵进程与SIGCHLD信号 12socket编程(七) TCP 11种状态 连接建立三次握手、连接终止四次握手 TIME_WAIT与SO_REUSEADDR SIGPIPE 13socket编程(八) 五种I/O模型 select 用select改进回射客户端程序 14socket编程(九) select 读、写、异常事件发生条件 用select改进回射服务器程序。 15socket编程(十) 用select改进第八章点对点聊天程序 16socket编程(十一) 套接字I/O超时设置方法 用select实现超时 read_timeout函数封装 write_timeout函数封装 accept_timeout函数封装 connect_timeout函数封装 17socket编程(十二) select限制 poll 18socket编程(十三) epoll使用 epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 UNIX域地址结构 UNIX域字节流回射客户/服务 UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信发展 进程间通信分类 进程间共享信息的三种方式 IPC对象的持续性 24进程间通信介绍(二) 死锁 信号量 PV原语 用PV原语解决司机与售票员问题 用PV原语解决民航售票问题 用PV原语解决汽车租赁问题 25System V消息队列(一) 消息队列 IPC对象数据结构 消息队列结构 消息队列在内核中的表示 消息队列函数 26System V消息队列(二) msgsnd函数 msgrcv函数 27System V消息队列(三) 消息队列实现回射客户/服务器 28共享内存介绍 共享内存 共享内存示意图 管道、消息队列与共享内存传递数据对比 mmap函数 munmap函数 msync函数 29System V共享内存 共享内存数据结构 共享内存函数 共享内存示例 30System V信号量(一) 信号量 信号量集结构 信号量集函数 信号量示例 31System V信号量(二) 用信号量实现进程互斥示例 32System V信号量(三) 用信号集解决哲学家就餐问题 33System V共享内存与信号量综合 用信号量解决生产者消费者问题 实现shmfifo 34POSIX消息队列 POSIX消息队列相关函数 POSIX消息队列示例 35POSIX共享内存 POSIX共享内存相关函数 POSIX共享内存示例 Linux网络编程之线程篇 36线程介绍 什么是线程 进程与线程 线程优缺点 线程模型 N:1用户线程模型 1:1核心线程模型 N:M混合线程模型 37POSIX线程(一) POSIX线程库相关函数 用线程实现回射客户/服务器 38POSIX线程(二) 线程属性 线程特定数据 39POSIX信号量与互斥锁 POSIX信号量相关函数 POSIX互斥锁相关函数 生产者消费者问题 自旋锁与读写锁介绍 40POSIX条件变量 条件变量 条件变量函数 条件变量使用规范 使用条件变量解决生产者消费者问题 41一个简单的线程池实现 线程池性能分析 线程池实现 网络编程, Linux, 密码

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值