一、引言

  欢迎大家来到和我一起编写Http服务器实现文件的上传和下载,现在我稍微回顾一下之前我说的,第一、二章说明说明了整体的HTTP走向,第三章实现底层的网络编程。接着这一章我想给大家讲的是请求获取,和响应发送的内容。这里主要讲解的响应内容,为什么?因为我们编写的是一个与浏览器交互的HTTP服务器,所以大多数的情况下我们只进行被动的应答。

  这就是一种"提问--回答"的问题。其实在讲解这章的时候,我本来准备给大家讲解一下Linux一些信号中断的问题。因为在网络层发送的时候,系统会发送一些信号给我们的应用程序,所以会导致我们的程序意外的终止。但当我写的这篇博客的时候我又放弃,我想在讲流程走向的时候再提一个中断捕获吧。在这个请求响应层的类其实真正的设计需要很多的内容,这里就是HttpResponse类和HttpRequest类的设计,在j2EE中,我们编写Servlet的时候就用到了这2个类,如HttpServletResquest,HttpServletResponse的类,如果对这里面的内容感兴趣,可以下载tomcat,在servlet-api.jar包里面有这些类。

  在本文的实现中,Request类只包含了一个获取请求头和解析头的一些方法。如何解析头,我在《Http服务器实现文件上传与下载(一)》已经讲解了,读者只需要对其封装一个类即可。

二、HttpRequest类

  请求消息的解析是通过被定义在命名空间为Http的类名为HttpRequest。这个类的构造函数接受一个套接字,就是跟我们连接的那个套接字,在网络层我们已经讲过了,然后在getHeader方法中调用server_read()获取请求头,然后通过Utils::parseHeader()函数进行解析。这样把解析的内容放入需要的string中,当前不太需要的直接在map里面。这里我直接贴出代码,大家看起来也比较容易。这里我在这一章节我主要讲解的是文件的下载,所以主要会对HttpResponse的类的分析,而HttpRequest类只贴出目前需要的内容。

头文件(include/httprequest.h)

 1 #ifndef HTTPREQUEST_H
2 #define HTTPREQUEST_H
3 #include "socket.h"
4 #include <map>
5 #include <string>
6 #include <fstream>
7 namespace Http{
8 class HttpRequest{
9 public:
10 HttpRequest(TCP::Socket &c);
11 virtual ~HttpRequest();
12 std::map<std::string,std::string> getHeader(int confd) ;
13 ......
14 protected:
15 private:
16 std::string method;
17 std::string url;
18 std::string host;
19 TCP::Socket &s;
20 };
21 }
22
23 #endif // HTTPREQUEST_H

源文件(src/httprequest.cpp)

 1 #include "httprequest.h"
2 #include "utils.h"
3 namespace Http{
4 HttpRequest::HttpRequest(TCP::Socket &c):s(c){
5 }
6
7 HttpRequest::~HttpRequest(){
8 }
9 std::map<std::string,std::string> HttpRequest::getHeader(int confd){
10 char recvBuf[1024];
11 memset(recvBuf,0,sizeof(recvBuf));
12 s.server_read(confd,recvBuf,1024);
13 std::cout<<recvBuf<<std::endl;
14 std::map<std::string,std::string> mp =Utils::parseHeader(recvBuf);
15 method =mp["Method"];
16 url=mp["Url"];
17 host=mp["Host"];
18 return mp;
19 }
20 ......
21 }

三、HttpResponse类

  当我们访问Http服务器的时候,浏览器显示可以下载的文件的内容,然后我们点击需要下载的文件,然后文件就可下载了。首先我点击这个文件这个URL时,浏览器给我们发送一些请求头,例如它发送一个为/download/HttpServer.zip这个URL,说明他需要下载的文件,而且该文件为HttpServer.zip。在上面我们已经可以用getHeader来捕获这个请求头,然后获取这个URL。之后服务端还是要发送一个响应头,告诉浏览器你的请求我们同意,请求头结束以空行为标记,接着就是具体的文件的内容了。

  在发送响应头时,还是需要发送协议版本,状态码,响应内容类型,文件的长度,文件断点下载等内容,或者传输的时候采用chunk传输,但是这里我采用文件的长度来标记。读者可以自行查看其它方式传输内容。特别要注意ed是一定要在响应头中指定传输实体的大小,否则客户端不知道什么时候结束,这时可能拒绝接收服务端发来的字节。在这个类中,请求下载的文件发送时,我采用sendFile这个函数,这个函数读取文件就是采用二进制的方式,并且在响应头中也告知浏览器以二进制的方式接收文件。这样都是以二进制的方式读取和发送文件才不会出现问题。sendLineFile 和sendIndexFile两者大致相同,都是采用ASCII文本的方式发送内容,这样比如HTML这些需要显示在浏览器的内容,可以通过这两个函数。通过函数名可知在sendLineFile会以文件行的方式读取,而sendIndexFile文件会把内容写在同一行上。例如:我们浏览器请求一个index.html的内容,这时采用2个sendLineFile和sendIndexFile的显示效果都是一样的,但是如果点击右键查看源码时,sendLineFile的内容是以源文件一样的,而sendIndexFile发送的内容会都在第一行,不会换行。

  说了这么多大家也比较清楚了,下面贴出具体一些代码。

头文件(include/httpresponse.h)

 1 #ifndef HTTPRESPONSE_H
2 #define HTTPRESPONSE_H
3 #include "socket.h"
4 #include<string>
5 #include<fstream>
6 #include<sstream>
7 #include<iterator>
8 #include<algorithm>
9 #include<time.h>
10 #include "utils.h"
11 namespace Http{
12 class HttpResponse{
13 public:
14 HttpResponse(TCP::Socket &c);
15 virtual ~HttpResponse();
16 ssize_t send(int confd,std::string content);
17 ssize_t sendIndexFile(int confd,std::string FileName);
18 ssize_t sendFile(int &confd,std::string FileName,int64_t pos);
19 ssize_t sendLineFile(int confd,std::string file);
20 void setProtocal(std::string);
21 void setStatusCode(std::string);
22 void setServerName(std::string);
23 void setContentType(std::string);
24 void setContentRange(std::string);
25 void setContentLength(int64_t);
26 protected:
27 std::string getHeader() const;
28 private:
29 std::string protocal;
30 std::string statusCode;
31 std::string serverName;
32 std::string contentType;
33 std::string contentLength;
34 std::string contentRange;
35 std::string connection;
36 std::string date;
37 TCP::Socket &s;
38 };
39 }
40 #endif // HTTPRESPONSE_H

源文件(src/httpresponse.cpp)

  1 #include "httpresponse.h"
2 namespace Http{
3 HttpResponse::HttpResponse(TCP::Socket &c):s(c){
4 protocal="HTTP/1.1";
5 statusCode="200 OK";
6 serverName="Server:(Unix)";
7 contentType="Content-type:text/html";
8 contentLength="Content-length:0";
9 contentRange="Content-Range:0-";
10 connection="Connection:Keep-Alive";
11 time_t timep;
12 time(&timep);
13 char s[50];
14 sprintf(s,ctime(&timep));
15 date="Date:"+std::string(s,s+(strlen(s)-1));
16 }
17
18 HttpResponse::~HttpResponse(){
19 }
20 void HttpResponse::setProtocal(std::string content){
21 protocal=content;
22 }
23 void HttpResponse::setStatusCode(std::string content){
24 statusCode=content;
25 }
26 void HttpResponse::setServerName(std::string content){
27 serverName=content;
28 }
29 void HttpResponse::setContentType(std::string content){
30 contentType="Content-type:"+content;
31 }
32 void HttpResponse::setContentLength(int64_t len){
33 contentLength="Content-length:"+Utils::toString(len);
34 }
35 void HttpResponse::setContentRange(std::string content){
36 contentRange="Content-Range:"+content;
37 }
38 std::string HttpResponse::getHeader() const{
39 std::string h1 =protocal+" "+statusCode+"\r\n";
40 std::string h2 =serverName+"\r\n";
41 std::string h3 =contentType+"\r\n";
42 std::string h4 =contentLength+"\r\n";
43 std::string h5=contentRange+"\r\n";
44 std::string h6=connection+"\r\n";
45 std::string h7=date+"\r\n\r\n";
46 return h1+h2+h3+h4+h5+h6+h7;
47 }
48 ssize_t HttpResponse::send(int confd,std::string content){
49 setContentType("application/octet-stream");
50 setContentLength(content.size());
51 std::string header=getHeader();
52 s.server_write(confd,(char*)header.c_str(),header.size());
53 ssize_t len =s.server_write(confd,(char*)content.c_str(),content.size());
54 s.server_close(confd);
55 return len;
56 }
57 ssize_t HttpResponse::sendLineFile(int confd,std::string file){
58 std::ifstream in(file.c_str());
59 in.seekg(0,std::ios::end);
60 int64_t len = in.tellg();
61 setContentLength(len);
62 std::string header=getHeader();
63 s.server_write(confd,(char*)header.c_str(),header.size());
64 in.seekg(0,std::ios::beg);
65 ssize_t n=0;
66 char buf[1024];
67 while(!in.eof()){
68 bzero(buf,sizeof(buf));
69 in.getline(buf,1024);
70 buf[strlen(buf)]='\n';
71 n+=s.server_write(confd,buf,in.gcount());
72 }
73 s.server_close(confd);
74 return n;
75 }
76 ssize_t HttpResponse::sendIndexFile(int confd,std::string file){
77 std::ifstream in(file.c_str());
78 in.seekg(0,std::ios::end);
79 int64_t len = in.tellg();
80 setContentLength(len);
81 std::string header=getHeader();
82 s.server_write(confd,(char*)header.c_str(),header.size());
83 in.seekg(0,std::ios::beg);
84 char buf[1024];
85 int sendCount=0;
86 while(!in.eof()){
87 memset(buf,0,sizeof(buf));
88 in.getline(buf,1024);
89 sendCount+=s.server_write(confd,buf,in.gcount());
90 }
91 s.server_close(confd);
92 return sendCount;
93 }
94 ssize_t HttpResponse::sendFile(int &confd,std::string fileName,int64_t pos){
95 std::ifstream in(fileName.c_str(),std::ios::binary);
96 in.seekg(0, std::ios::end);
97 std::streampos ps = in.tellg();
98 int64_t len=ps-pos;
99 if(pos!=0){
100 setStatusCode("206 Partial Content");
101 }
102 setContentType("application/octet-stream");
103 setContentLength(len);
104 std::string content="bytes";
105 content+=" "+Utils::toString(pos)+"-"+Utils::toString((int64_t)ps-1)+"/"+Utils::toString(len);
106 setContentRange(content);
107 std::string header=getHeader();
108 std::cout<<header<<std::endl;
109 s.server_write(confd,(char*)header.c_str(),header.size());
110 in.seekg(pos,std::ios::beg);
111 char buf[1024];
112 ssize_t n=0;
113 while(!in.eof()){
114 in.read(buf,1024);
115 n+=s.server_write(confd,buf,in.gcount());
116 }
117 s.server_close(confd);
118 return n;
119 }
120 }

  在上面响应头中Content-Range:这个字段,表示文件内容的范围,在一般情况下都是从0到lenth(file)-1。如果在之前已经下了一些内容后,如果是断点续下载时,浏览器在请求头中有Range知道,表示从Range的开始字节传输,而我们服务器指定Content-Range为Range字段开始,接着发送这些内容即可,实现文件的断点下载。接下来的内容请大家看《Http服务器实现文件上传与下载(五)》。

Http服务器实现文件上传与下载(四)的更多相关文章

  1. Http服务器实现文件上传与下载(三)

    一.引言 在前2章的内容基本上已经讲解了整个的大致流程.在设计Http服务器时,我设计为四层的结构,最底层是网络传输层,就是socket编程.接着一层是请求和响应层,叫做Request和Respons ...

  2. java使用Jsch实现远程操作linux服务器进行文件上传、下载,删除和显示目录信息

    1.java使用Jsch实现远程操作linux服务器进行文件上传.下载,删除和显示目录信息. 参考链接:https://www.cnblogs.com/longyg/archive/2012/06/2 ...

  3. Http服务器实现文件上传与下载(五)

    一.引言 欢迎大家和我一起编写Http服务器实现文件的上传和下载,现在我回顾一下在上一章节中提到的一些内容,之前我已经提到过文件的下载,在文件的下载中也提到了文件的续下载只需要在响应头中填写Conte ...

  4. Http服务器实现文件上传与下载(一)

    一.引言 大家都知道web编程的协议就是http协议,称为超文本传输协议.在J2EE中我们可以很快的实现一个Web工程,但在C++中就不是非常的迅速,原因无非就是底层的socket网络编写需要自己完成 ...

  5. Http服务器实现文件上传与下载(二)

    一.引言 欢迎大家接着看我的博客,如何大家有什么想法的话回复我哦,闲话不多聊了,接着上一讲的内容来说吧,在上一节中已经讲到了请求头字符串的解析,并且在解析中我我们已经获取了url.就是上节中提到的/d ...

  6. java代码实现ftp服务器的文件上传和下载

    java代码实现文件上传到ftp服务器: 1:ftp服务器安装: 2:ftp服务器的配置: 启动成功: 2:客户端:代码实现文件的上传与下载: 1:依赖jar包: 2:sftpTools   工具类: ...

  7. 【问题解决方案】Xshell连接服务器并实现上传和下载文件

    参考链接: Xshell连接服务器并实现上传和下载文件 第一步:xshell登录完成 略 第二步: 在服务器安装lrzsz 如果服务器的操作系统是 CentOS,则输入命令[yum install l ...

  8. java web学习总结(二十四) -------------------Servlet文件上传和下载的实现

    在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用 ...

  9. (转载)JavaWeb学习总结(五十)——文件上传和下载

    源地址:http://www.cnblogs.com/xdp-gacl/p/4200090.html 在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传 ...

随机推荐

  1. Hibernate 入门示例

    版权声明:本文为博主原创文章,如需转载请标注转载地址 博客地址:http://www.cnblogs.com/caoyc/p/5593406.html  环境: myelipse2015+Hibern ...

  2. Java的内存机制(转)

    0.参考资料: http://www.j2megame.org/index.php/content/view/2246/125.html 1.Java的内存机制 Java 把内存划分成两种:一种是栈内 ...

  3. struct2常用标签

    Struts2常用标签总结 一 介绍 1.Struts2的作用    Struts2标签库提供了主题.模板支持,极大地简化了视图页面的编写,而且,struts2的主题.模板都提供了很好的扩展性.实现了 ...

  4. easyui tree自定义属性用法

    easyui为树显示提供了以下属性, id:节点id,这个很重要到加载远程服务器数据 which is important to load remote data text: 显示的节点文本 stat ...

  5. C++的泛型编程方式

    1.使用类模板创建数组 下面这段代码:是创建一个元素为 T 类型的数组. #pragma once template<class T> class MyArray { public: // ...

  6. LinkedList的一种错误使用方法

    近期在做图像处理的时候.发现某一段代码很的慢.慢得让人无法接受.基本的代码是顺序訪问一个LinkedList的元素,效果是随着index的变大,速度越来越慢,list的元素个数在百万以上.找到原因,分 ...

  7. MySQL主从同步那点事儿

    一.前言 关于mysql主从同步,相信大家都不陌生,随着系统应用访问量逐渐增大,单台数据库读写访问压力也随之增大,当读写访问达到一定瓶颈时,将数据库的读写效率骤然下降,甚至不可用;为了解决此类问题,通 ...

  8. Linux编程之判断磁盘空间大小

    一.引言 在开发过程中,经常会碰到这样的情况,在往指定目录下拷贝文件时,需要考虑到磁盘空间的大小是否足够来决定什么时候暂停自己的程序 二.用的函数 <sys/statfs.h> int s ...

  9. java web 打水印

    /** * 把图片印刷到图片上 * * @param pressImg -- * 水印文件 * @param targetinp -- * 目标文件 * @param x * --x坐标 * @par ...

  10. 解决linux下/etc/rc.local开机器不执行的原因

    前不久因项目需要写了开机启动其他程序的shell脚本,因工作忙,调试完给了技术支持人员,也没去注意过. 到后来,有几台服务器突然被重启了,这时候领导问,怎么开机启动的脚本没起作用,还被批了一顿,哎,做 ...