从零开始学写HTTP服务器(三)请求资源
来源:互联网 发布:贵州云博大数据 编辑:程序博客网 时间:2024/06/10 18:40
客户端通过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.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 <iostream>#include <fstream>#include <string>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<<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))是给互联网上传输的内容赋予的分类类型。一份内容的互联网媒体类型是由其文件格式与内容决定的。互联网媒体类型与文件拓展名相对应,因此计算机系统常常通过拓展名来确定一个文件的媒体类型并决定与其相关联的软件。互联网媒体类型的分类标准由互联网号码分配局(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 <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 512int 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);}voidBind(int fd, const struct sockaddr *sa, socklen_t salen){ if (bind(fd, sa, salen) < 0) { printf("bind error\r\n"); exit(-1); }}voidListen(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 ; }}intAccept(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
chrome测试:
(五)参考
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服务器(三)请求资源
- 从零开始学写HTTP服务器(一)http协议简介
- 从零开始学写HTTP服务器(四)IO复用
- 从零开始学写HTTP服务器(七)muduo+tinyhttpd
- 从零开始学写HTTP服务器(二)socket编程实现简单的http server
- 从零开始学写HTTP服务器(五)IO复用使用epoll
- 从零开始学写HTTP服务器(六)使用muduo网络库
- 从零开始学Android(三)
- 从零开始学Makefile(三)
- 从零开始写PHP (三)
- 从零开始学写WinForm(一)
- 从零开始学HTTP (一)网络基础
- 从零开始学HTML(三)--HTML 元素
- 【从零开始学NGUI 】 (三)Button
- 从零开始学Hadoop----浅析HDFS(三)
- 从零开始学jBPM6(三)- Eclipse
- Spark从零开始学 三
- Photon Server游戏服务器从零开始学习(三)在Unity3D中创建客户端与服务器连接及发送请求
- 一些小效果的实现
- 阿里云redis服务器安装配置
- Linux实现负载均衡
- 安全算法
- hudson安装以及使用
- 从零开始学写HTTP服务器(三)请求资源
- C++ const 用法总结
- 算法入门经典2 第3章解题报告
- 【Leetcode】477. Total Hamming Distance
- 求一个int数是否是4的幂
- python3 串口通讯
- MAC10.12Caps Lock失灵
- SpringMVC 菜鸟教程 3 文件上传 CommonsMultipartResolver
- 大话设计模式-工厂方法模式