互联网已经发展了20多年,web技术早已经不是什么新消息,现在更多的程序员讨论的都是web框架,技术框架,高可用框架等。在框架的背后,其实还是要了解最底层的核心内容,比如TCP/IP协议怎么回事,HTTP协议是怎样的,这些都应该了解!高级语言的设计,让很多程序员已经忘记了这些协议的内涵,本文带领大家了解一下HTTP协议。

HTTP是超文本传输协议,它出于TCP/IP协议的应用层,侧重于表示,大白话就是它是浏览器和后端服务器通信时的消息格式规定,在浏览器请求消息时应遵守HTTP请求消息的要求,而服务器响应浏览器的请求时,应发送符合HTTP响应消息要求的消息格式。它的主要目的就是为了表述清楚,浏览器到底了请求了什么。

在浏览器请求消息里,可以认为分为四部分分,内容如下:

·请求行

·请求头

·空行

·请求附加信息(可无)

具体可以看看下图:

当浏览器输入一个网址时,服务器端收到的就是这样一大坨信息,可以对应为前面的四部分。其中最为关键的就是请求行一部分,请求行按空格拆分后又可以分为三部分:

对于请求头来说,就是多组键值对的集合,其中User-Agent尤为关键,它可以标识出请求的浏览器是哪款,对于想写爬虫的童鞋,这里一定要多关注的。在HTTP里要求,所有的换行符都是“rn”,这是由于历史原因造成的,所以我们看到的每一行请求后面都是rn,包括那个空行也是!

作为服务器接收到这样一个请求后,就要分析浏览器到底要请求什么,最关键的就是请求行的信息,它里面有方法和资源。对于GET方法,就是代表要获取这个资源,这个资源可以是一个网页,也可以是一个文本文件,也可以是个图片或者mp3文件。

服务器在明确了对方的请求后,需要组织一个响应消息发送给浏览器,这个响应消息同样要遵循HTTP协议的响应消息要求,结构如下:

·响应行

·响应头

·空行

·请求资源的正文

具体可以参考下图:

响应头同样分为了3部分:

·协议版本

·错误码

·错误消息

错误码已经提前约定好,比如200代表正常,3开头的代表网址重定向,404代表资源不存在,5开头代表服务器有问题等等。

在了解了上述内容后,我们来尝试用Go语言编写一个web文件服务器,支持本地一个目录的web访问。为了更好的理解HTTP协议,我们采用TCP的方式建立连接,手动实现HTTP协议的消息格式!

步骤可以如下:

·切换工作目录为“外部访问的”目录

·启动侦听

·循环等待新连接

·对于新连接启动一个goroutine处理连接

func main() {//切换工作目录,便于读取对应的请求资源,一定要存在os.Chdir("/Users/yekai/Downloads/webpath")listener, err := net.Listen("tcp", ":8888")if err != nil {log.Panic("Failed to Listen ", err)}defer listener.Close()for {//得到新连接conn, err := listener.Accept()if err != nil {fmt.Println("Failed to Accept ", err)continue}//启动goroutinego handle_conn(conn)}
}

接下来考虑handle_conn,这个goroutine的逻辑,可以是这样:

·循环等待对方请求消息

·解析请求消息

·获得请求行

·分析请求行

·分析请求资源

·如果是文件,则发送文件给对方

·如果是目录,则读取目录内容,组合一个消息给对方

·如果资源不存在,则发送对方一个404错误

这里需要特别提醒的是,响应消息要有HTTP协议要求。

先写壳子

func handle_conn(conn net.Conn) {defer conn.Close()reader := bufio.NewReader(conn)//定义正则表达式解析methdreg := regexp.MustCompile("([a-zA-Z]+) (/.*) (.+)rn")for {//循环读取行msg, err := reader.ReadString('n')if err != nil {fmt.Println("Failed to ReadString", err)break}requests := methdreg.FindAllStringSubmatch(msg, -1)if len(requests) > 0 {fmt.Println(requests[0], len(requests[0]))//顺利得到行后再用goroutine处理go handle_method(conn, requests[0][1], requests[0][2])}}}

接下来再考虑实现handle_method

func handle_method(conn net.Conn, method string, url string) error {fmt.Println("method=", method, " url=", url)if method == "GET" || method == "get" {//处理get方法 //为什么要加 . 呢?考虑请求资源的信息是什么样的path := "." + url//读取资源信息,间接判断出是否存在info, err := os.Stat(path)if err != nil {fmt.Println("Failed to Stat", err)errinfo, _ := os.Stat("error.html")//发送HTTP协议头部信息sendContext(conn, "error.html", "404", "NOT FOUND", errinfo.Size())//发送文件sendfile(conn, "error.html")return err}if info.IsDir() {//处理目录fd, err := os.Open(path)if err != nil {fmt.Println("Failed to Open ", path, err)return err}infos, err := fd.Readdir(-1)if err != nil {fmt.Println("Failed to Readdir ", err)return err}sendbuf := ""for _, v := range infos {//组织数据列表if v.IsDir() {sendbuf += fmt.Sprintf("<li><a href='%s/'>%s</a></li>", v.Name(), v.Name())} else {sendbuf += fmt.Sprintf("<li><a href='%s'>%s</a></li>", v.Name(), v.Name())}}sendbuf = fmt.Sprintf(index_html, sendbuf)length := len([]byte(sendbuf))sendContext(conn, path, "200", "Ok", int64(length))io.WriteString(conn, sendbuf)} else {//发送HTTP协议头部信息sendContext(conn, path, "200", "Ok", info.Size())//发送文件sendfile(conn, path)}}return nil
}

编写的发送HTTP协议头以及发送文件的函数实现如下,逻辑并不复杂。

    //按文件类型分类
func getFileType(filename string) string {filetype := "text/html;charset=utf-8"if strings.HasSuffix(filename, ".jpg") {filetype = "image/jpeg"} else if strings.HasSuffix(filename, ".gif") {filetype = "image/gif"}return filetype
}func sendContext(conn net.Conn, filename, code, codemsg string, length int64) error {io.WriteString(conn, "HTTP/1.1 "+code+" "+codemsg+" rn")sendbuf := fmt.Sprintf("Content-Type: %srn", getFileType(filename))io.WriteString(conn, sendbuf)sendbuf = fmt.Sprintf("Content-Len: %drn", length)io.WriteString(conn, sendbuf)io.WriteString(conn, "rn")return nil
}func sendfile(conn net.Conn, filename string) error {fd, err := os.Open(filename)if err != nil {fmt.Println("Failed to Open ", err, filename)return err}defer fd.Close()//流拷贝n, err := io.Copy(conn, fd)fmt.Println("send file ok, size = ", n)return err
}

这样用了150多行,实现了一个HTTP版的文件服务器,可以启动起来:

localhost:study yekai$ go run webserver.go

此后可以在浏览器里请求:localhost:8888 将看到

对于超链接的部分,可以继续点击请求。这个代码就编写到这里,其实如果要用Go语言来实现一个HTTP版的文件服务器,一行代码就搞定了!

http.ListenAndServe(":8899", http.FileServer(http.Dir("/Users/yekai/Downloads/webpath")))

既然如此,为什么还要如此费力的去编写一个150多行的代码呢?主要是为了让读者连接HTTP协议的细节!

关注更多请锁定gzh:柏链学习社

http代码_一行代码就可以实现HTTP文件服务器,他为什么写了150行?相关推荐

  1. python字符串逆序输出代码_一行代码实现字符串逆序输出

    字符串逆序实现三部曲: 1.将字符串转为数组,一个字符为数组的一个元素: 2.将数组倒置: 3.再将数组元素拼接为字符串. 方法一: var str = "naYgniYgnehZ" ...

  2. 网站变灰代码,一行代码让网站整体变灰,wordpress网站一行代码全站变灰教程

    在遇到特殊情况的时候,我们作为站长需要紧急将网站变灰的需求,在此小编给大家总结了几种方法,通过简单修改一下站点样式即可实现.一段代码让网站整体变灰.这里主要介绍的利用 filter: grayscal ...

  3. mapbox 修改初始位置_一行代码教你如何随心所欲初始化Bert参数(附Pytorch代码详细解读)...

    微信公众号:NLP从入门到放弃 微信文章在这里(排版更漂亮,但是内置链接不太行,看大家喜欢哪个点哪个看吧): 一行代码带你随心所欲重新初始化bert的参数(附Pytorch代码详细解读)​mp.wei ...

  4. 局域网怎么查看单位摄像头_一行代码,实现多平台文件查看传输!

    几个不错的通过网站传输文件的工具,其实了解python的朋友可能都知道,借助它,只需要输入一行代码,就可以将电脑变成一台服务器,局域网内的任何能上网的工具都能够查看和下载电脑中的文件. 方法很简单,可 ...

  5. python童年_一行代码玩童年小游戏

    首先,我们要打开cmd,以此下载freegames这个包.有一些新手小白可能会问:cmd是什么-- 打开cmd 首先按住笔记本电脑(或者台式机)的win键(就是四个小方块组成一个梯形的键),接着按下r ...

  6. python装b代码_一行python代码带你装B

    一行代码,python就能让你玩出花来.今天给大家介绍几个有趣的一行代码. 1.心形字符,全中文的话可能会变形,大家可以试试中英文搭配. print('\n'.join([''.join([('Ilo ...

  7. datatables 一行数据生成两行_一行代码搞定分组回归

    写 在前面 在目前为止所有小伙伴们向大猫请教过的R问题中,大猫总结了最常遇见同时也是比较难的三个问题,分别是(1)事件研究法:(2)分组回归:(3)滚动回归.事件研究法在第一期中已经讲述,本期我们就来 ...

  8. 灰色的rgb值_一行代码实现图片的灰色效果

    今天是清明节,警笛长鸣,万众默哀.送别英雄,生活继续. 大部分的app配图,今天都是灰色.这种图片的转换在python里非常容易实现.一行代码搞定. 下面是DE8UG上次说个故事:再见VB.你好,精彩 ...

  9. python好玩的代码_一行 Python 能实现什么丧心病狂的功能?

    能够把自身代码打印出来的程序,叫做Quine.下面是python的一行quine: ​有人说有分号不算一行,无分号版: 其实,如果你用程序语言的名字+quine作为关键字去搜索,你能找到各种语言实现的 ...

最新文章

  1. Java学习的30个目标以及系统架构师推荐的书
  2. 科普帖:Linux操作系统
  3. python库——h5py入门讲解
  4. linux删除jpeg动态库,linux如何不用的删除动态库
  5. 让你的linux操作系统更加安全
  6. Silverlight+WCF 新手实例 象棋 该谁下棋-A下B停(二十八)
  7. 运维工程师mysql面试题及答案_系统运维工程师面试题及参考答案
  8. 微信公众号支付开发(java)实例详解
  9. chronodex怎么用_滴答清单使用全攻略:如何把手帐搬到滴答清单上,提升效率?...
  10. MIMO系统信道容量分析
  11. 《自控力》第五章读书笔记
  12. mysql ping命令_MySQL常用命令总结
  13. 中国股市实行T+1,这是保护散户还是坑散户?
  14. 学生使用计算机中怎么关机,学生用计算器咋关机
  15. 参考文献起止页码怎么写_怎么看论文的起止页码
  16. 十月3倍销量于特斯拉的比亚迪,新增量在何处?
  17. Ant安装及环境变量配置+邮件配置+Ant生成接口测试报告
  18. 无人驾驶汽车是如何进行行为决策的?
  19. Oracle Distilled网站下的TimesTen相关帖子
  20. 搜索引擎的原理,架构与细节

热门文章

  1. 【开卷故意】JAVA正則表達式模版
  2. openstack kvm 虚拟机磁盘差异衍生
  3. crm采用soap删除记录
  4. 切割日志 python版
  5. Linux内核编译:很少有人提及的一些内容
  6. Base64编码解码与实现
  7. 对命令行程序调用及其结果显示的一点补充
  8. ZZULIOJ 1123: 最佳校友
  9. 支持ie8的时分秒的html,兼容ie8的漂亮jQuery计时器插件
  10. 信息学奥赛一本通 2019:【例4.4】求阶乘