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

一、引言

  大家都知道web编程的协议就是http协议,称为超文本传输协议。在J2EE中我们可以很快的实现一个Web工程,但在C++中就不是非常的迅速,原因无非就是底层的socket网络编写需要自己完成,上层的http协议需要我们自己完成,用户接口需要我们自己完成,如何高效和设计一个框架都是非常困难的一件事情。但这些事情Java已经在底层为我们封装好了,而我们仅仅只是在做业务层上的事情吧了。

  在本Http服务器实现中,利用C++库和socket原套接字编程和pthread线程编写。拒绝使用第三方库。因为主要是让大家知道基本的实现方式,除去一些安全、高效等特性,但是不管怎么样,第三方商业库的基本原理还是一致的,只是他们对其进行了优化而已。在开始的编写时,我不会全部的简介Http的协议的内容,这样太枯燥了,我仅仅解释一些下面需要用到的协议字段。

  在写本文的时候,之前也有些迷惑,C++到底能干啥,到网上一搜,无非就是能开发游戏,嵌入式编程,写服务器等等。接着如果问如何编写一个服务器的话,那么这些网络水人又会告诉你,你先把基础学好,看看什么书,之后你就知道了,我只能呵呵了,在无目的的学习中,尽管看了你也不知道如何写的,因为尽管你知道一些大概,但是没有一个人领导你入门,我们还是无法编写一个我们自己想要的东西,我写这篇博客主要是做一个小小的敲门砖吧,尽管网上有许多博客,关于如何编写HTTP服务器的,但是要不是第三方库acl,要么就是短短的几行代码,要么就是加入了微软的一些C#内容或者MFC,这些在我看来只是一些无关紧要的东西,加入后或许界面上你很舒服,但是大大增加了我们的学习成本,因为这些界面上的代码改变了我们所知道的程序流程走向,还有一些界面代码和核心代码的混合,非常不利于学习。

二、HTTP协议

在大家在浏览器的url输入栏上输入http://10.1.18.4/doing时。浏览器向10.1.18.4服务器80端口的进程发送了如下的一个协议头,它是一个文本字符串。每行以\r\n结束。表示回车换行。

1 GET /doing HTTP/1.1
2 Host: 10.1.18.4
3 User-Agent: Mozilla/5.0 (Windows NT 6.2; rv:40.0) Gecko/20100101 Firefox/40.0
4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
6 Accept-Encoding: gzip, deflate
7 Referer: http://10.1.18.4/
8 Connection: keep-alive

  所以知道其实我们发送了一个URL请求,其实被转化为了一个如上的一些字符串。在这里我简单的解释一下这个协议头表示什么,因为在网上你可以找到非常多的信息来解释它们。

  1)第一行中 GET /doing HTTP1.1 表示请求的方式是GET,URL是/doing ,HTTP协议的版本是1.1

  2)第二行中 Host 就是服务器的IP

  3)第三行中 User-Agent代表着你使用的是什么浏览器在什么系统上运行的。从上本可以这条信息显示是window上火狐浏览器发出的请求头

  4)第四行中Accept代表着该浏览器可以接受的信息格式,可以是文本,html,或者应用文件(二进制文件)。其中q代表权重,表示更愿意接受前面的信息。还有一些其他的内容,读者可以自己百度。

  5)以下的一些信息中,没有什么用到,我就不解释,看文本意义也大概知道一些信息。详细的请搜索网络。

在最重要的是一本请求头什么时候表示结束呢,那就是一个空行表示结束。其实就是"\r\n"结束。

  说了这么多可能大家还是有点迷糊,知道这些那么在程序中又是怎么实现的呢。当初我也迷惑,现在我提出一个最简单的一种实现,就是直接连接一个字符串即可。在实际实现中我对其进行了分解,但是现在,我解释为如下编写程序:

1 char *str= "GET /doing HTTP/1.1\r\n\
2 Host: 10.1.18.4\r\n3 User-Agent: Mozilla/5.0 (Windows NT 6.2; rv:40.0) Gecko/20100101 Firefox/40.0\r\n4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*//*;q=0.8\r\n\
5 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3\r\n6 Accept-Encoding: gzip, deflate\r\n7 Referer: http:http://10.1.18.4/\r\n\
8 Connection: keep-alive\r\n9 Range: bytes=14584264-\r\n\r\n" ;

可能上面的协议内容跟之前的有点不一样,没关系,我只是截取了一些内容进行输入。很简单就是C语言的char*字符串。在没一行的的结尾都都有一个‘\‘,表示表示换行输入,去掉也行,需要把器内容写到一行上,是C语言语法,不懂的读者可以自己查阅C语言的字符串。我想说的是在每行的结尾都有一个\r\n。这两个转义字符就是代表回车换行。并且在第9行有2个\r\n,最后一个代表着空行,意思是说告诉服务器我的协议头到此位置。

  为什么需要一个空行呢,这里就有一个网络编程的小小信息。在socket TCP流编程中,比如你调用了write或read函数,内部不是一次性接受或者发送所有的信息。所以当我们发送上述的str的时候,不一定一次全部的发送,那么服务端就不知道什么时候结尾了。所以我们需要HTTP规定以空行作为结尾代表着协议头的结束。

  接下面了来就是我们编写的服务器接受到这个字符串。并且以空行表示接受到整个协议头,然后对其进行解析。下面就是解析这段字符串的代码,在工程中我对其封装,但是现在我们只要知道实现解析功能即可。

 1 #include <iostream>
 2 #include <cstring>
 3 #include <vector>
 4 #include <map>
 5 #include <algorithm>
 6 using namespace std;
 7
 8 char *str= "GET /download/JBPM4S.tt HTTP/1.1\r\n\
 9 Host: 10.1.18.4\r\n10 User-Agent: Mozilla/5.0 (Windows NT 6.2; rv:40.0) Gecko/20100101 Firefox/40.0\r\n11 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*//*;q=0.8\r\n\
12 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3\r\n13 Accept-Encoding: gzip, deflate\r\n14 Referer: http:http://10.1.18.4/\r\n\
15 Connection: keep-alive\r\n16 Range: bytes=14584264-\r\n\r\n" ;
17
18 string& ltrim(string &str) {
19     string::iterator p = find_if(str.begin(), str.end(), not1(ptr_fun<int, int>(isspace)));
20     str.erase(str.begin(), p);
21     return str;
22 }
23
24 string& rtrim(string &str) {
25     string::reverse_iterator p = find_if(str.rbegin(), str.rend(), not1(ptr_fun<int , int>(isspace)));
26     str.erase(p.base(), str.end());
27     return str;
28 }
29
30 string& trim(string &str) {
31     ltrim(rtrim(str));
32     return str;
33 }
34 string getContent(string& str,int start,char c,int &pos){
35     int i=start;
36     int len=str.size();
37     while(i<len&&str[i]!=c){
38         i++;
39     }
40     pos=i;
41     return str.substr(start,i-start);
42 }
43 map<string,string> parseHeader(char* str){
44     int len=strlen(str);
45     vector<string> vs;
46     int i=0;
47     while(i<len){
48         if(str[i]!=‘\r‘){
49             int j=i;
50             while(i<len&& str[i]!=‘\r‘)
51                 i++;
52             vs.push_back(string(str+j,str+i));
53         }else{
54             i+=2;
55         }
56     }
57     int pos;
58     string method=getContent(vs[0],0,‘ ‘,pos);
59     string url=getContent(vs[0],method.size()+1,‘ ‘,pos);
60     map<string,string> mp;
61     mp["Method"]=method;
62     mp["Url"]=url;
63     for(int i=1;i<vs.size();i++){
64         string key=getContent(vs[i],0,‘:‘,pos);
65         string value=vs[i].substr(pos+1);
66         mp[key]=trim(value);
67     }
68     return mp;
69 }
70
71 int main(int argc, char **argv)
72 {
73     map<string,string> mp =parseHeader(str);
74     for(map<string,string>::const_iterator it=mp.begin();it!=mp.end();++it){
75         cout<<it->first <<"   "<<it->second<<endl;
76     }
77     return 0;
78 }

把一些信息解析都放到了一个map里面。这里的解析是先处理每一行,然后再对每一行进行解析。可能这样的处理方式有点慢,但是没什么关系,原因是字符串反正比较短,在大并发下效率不会影响太大,如果大家有什么更好的解析方式,可以回复我。

在服务端解析头信息后,我们可以得到/doing这个url,这样我们服务请就可以把客户端需要的内容返回给客户端了,这里就有浏览器请求的内容是否合法是否存在这些信息。就要在相应的响应头中说明,在《Http服务器实现文件上传与下载(二)》中会进行说明。

欢迎大家一起探讨这些问题。有什么想法的人给我回复,我们一起学习,一起进步哦。

  

时间: 2024-09-29 18:00:15

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

Http服务器实现文件上传与下载(四)

一.引言 欢迎大家来到和我一起编写Http服务器实现文件的上传和下载,现在我稍微回顾一下之前我说的,第一.二章说明说明了整体的HTTP走向,第三章实现底层的网络编程.接着这一章我想给大家讲的是请求获取,和响应发送的内容.这里主要讲解的响应内容,为什么?因为我们编写的是一个与浏览器交互的HTTP服务器,所以大多数的情况下我们只进行被动的应答. 这就是一种"提问--回答"的问题.其实在讲解这章的时候,我本来准备给大家讲解一下Linux一些信号中断的问题.因为在网络层发送的时候,系统会发送一

C# 之 FTP服务器中文件上传与下载(三)

通过上一篇博客<C# 之 FTP服务器中文件上传与下载(二)>,我们已经实现将文件上传到我们创建的FTP服务器.今天我们就一起来看看怎么样实现从FTP服务器中下载我们所需要的文件.         我们想实现的效果是在页面上有一个超链接,超链接显示为我们想要下载的文件名.点击该文件名进入下载页面.首先我们在前台插入一个超链接,但是这个超链接为后台拼接的超链接. using System; using System.Collections.Generic; using System.Linq;

C# 之 FTP服务器中文件上传与下载(四)

        通过上一篇博客<C# 之 FTP服务器中文件上传与下载(三)>,我们已经实现将文件从我们创建的FTP服务器中下载到本地.今天就最后给大家讲解一下怎么对FTP中的文件进行重命名和删除操作. 首先在前台添加两个重命名和删除的按钮 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Defaul

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

一.引言 欢迎大家和我一起编写Http服务器实现文件的上传和下载,现在我回顾一下在上一章节中提到的一些内容,之前我已经提到过文件的下载,在文件的下载中也提到了文件的续下载只需要在响应头中填写Content-Range这一字段,并且服务器的文件指针指向读取的指定位置开始读取传输.在这一章节中我讲讲解文件的上传这一功能,讲完这一章节,大致的功能也全部完成,接着就是上面文件控制模块和一些资源模块. 在文件的上传中主要以HttpRequest类为主,在考虑文件的上传时我一点迷惑,到底把文件的上传功能是放

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

1.java使用Jsch实现远程操作linux服务器进行文件上传.下载,删除和显示目录信息. 参考链接:https://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html https://www.cnblogs.com/longyg/archive/2012/06/25/2561332.html https://www.cnblogs.com/qdwyg2013/p/5650764.html#top 引入jar包的maven依赖如下所示:

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

一.引言 欢迎大家接着看我的博客,如何大家有什么想法的话回复我哦,闲话不多聊了,接着上一讲的内容来说吧,在上一节中已经讲到了请求头字符串的解析,并且在解析中我我们已经获取了url.就是上节中提到的/doing.当浏览器发送了/doing请求后,这是的与服务器的连接并没有中断,原因无他,就是浏览器等待接受服务端发来的信息,然后他对信息进行解析并显示在浏览器界面上.在编写Http服务器时的C/S结构时,我只编写服务端server,客户端直接用现成的浏览器即可. 当服务端找到浏览器需要的内容的时候,现

Android与Asp.Net Web服务器的文件上传下载BUG汇总【更新】

遇到的问题: 1.java.io.IOException: open failed: EINVAL (Invalid argument)异常,在模拟器中的sd卡创建文件夹和文件时报错 出错原因可能是:(1)文件名称中含有不符合规范的字符,比如“:”,“?”或者空格等.(2)需要先创建文件夹目录再创建文件,不能直接创建文件. 2. android.os.NetworkOnMainThreadException异常,从服务器请求数据后,写入文件时报错 出错原因:在主线程内执行了访问http的操作,最

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

在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件.这个common-fileupload上传组件的jar包可以去apache官网上面下载,也可以在struts的lib文件夹下面找到,stru

深入分析JavaWeb Item40 -- 文件上传和下载

在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件.这个common-fileupload上传组件的jar包可以去apache官网上面下载,也可以在struts的lib文件夹下面找到,stru