客户端通过URI,Content-Type等请求头的内容访问服务端的特定实体,今天主要学习这部分内容。
服务端收到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.
将内容输出到文件,用法跟标准的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.”输出。
从文件输入内容到程序,跟标准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;
}
我们直到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;
互联网媒体类型(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