蹒跚学Go-Http编程+爬虫

HTTP

HTTP(Hyper Text Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的一种网络协议,定义了客户端和服务器端之间请求与响应的传输标准。
Go 语言标准库内建提供了net/http包,涵盖了HTTP客户端和服务器端的具体实现。
使用net/htp包,可以很方便地编写HTTP客户端或服务器端的程序。

  • HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。

地址

URL全称为Unique Resource Location,用来表示网络资源,可以理解为网络文件路径。
基本URL的结构包含模式(协议)、服务器名称(IP地址)、路径和文件名。常见的协议/模式如http、https、ftp等。服务器的名称或IP地址后面有时还跟一个冒号和一个端口号。再后面是到达这个文件的路径和文件本身的名称

  • URL的长度有限制,不同的服务器的限制值不太相同,但是不能无限长

  • http://主机名:端口号/path?表单

正常的相应包的例子

类型 解析
HTTP/1.1 200 OK 状态行
Server: nginx 服务器使用的WEB软件名及版本
Content-Type: text/html; charset=UTF-8 服务器发送信息的类型
Connection: keep-alive 保持连接状态
Set-Cookie: PHPSESSID=mjup58ggbefu7ni9jea7908kub; path=/; HttpOnly
Cache-Control: no-cache
Date: Wed, 14 Nov 2018 08:27:32 GMT 发送时间
Content-Length: 99324 主体内容长度
空行用来分割消息头和主体
消息体**
func main() {
    //创建监听socket
    listener,err := net.Listen("tcp",":8000")
    if err != nil {
        fmt.Println("Listener err",err)
        return
    }
    defer listener.Close()
    //阻塞等待客户端连接
    conn,err := listener.Accept()
    if err != nil {
        fmt.Println("Accept err :",err)
        return
    }
    defer conn.Close()
    fmt.Println(conn.RemoteAddr().String(),"连接成功")
    buf := make([]byte,2048)
    n,err:=conn.Read(buf)
    result := buf[:n]
    fmt.Printf("#\n%s#",string(result))
}
//在浏览器中输出127.0.0.1:8000
----------------------------------------
127.0.0.1:64665 连接成功
#
GET / HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: __guid=96992031.2518852697064897000.1530511322665.4133

#

请求行

  • GET
    • 当客户端要从服务器中读取某个资源时,使用GET 方法。GET 方法要求服务器将URL 定位的资源放在响应报文的数据部分,回送给客户端,即向服务器请求某个资源。

    • 使用GET方法时,请求参数和对应的值附加在 URL 后面,利用一个问号(“?”)代表URL 的结尾与请求参数的开始,传递参数长度受限制,因此GET方法不适合用于上传数据。

    • 通过GET方法来获取网页时,参数会显示在浏览器地址栏上,因此保密性很差。

  • POST

    • 当客户端给服务器提供信息较多时可以使用POST 方法,POST 方法向服务器提交数据,比如完成表单数据的提交,将数据提交给服务器处理。

    • GET 一般用于获取/查询资源信息,POST 会附带用户数据,一般用于更新资源信息。POST 方法将请求参数封装在HTTP 请求数据中,而且长度没有限制,因为POST携带的数据,在HTTP的请求正文中,以名称/值的形式出现,可以传输大量数据。

请求头

请求头部为请求报文添加了一些附加信息,由“名/值”对组成,每行一对,名和值之间使用冒号分隔。请求头部通知服务器有关于客户端请求的信息

请求头 含义
User-Agent 请求的浏览器类型
Accept 客户端可识别的响应内容类型列表,星号“ * ”用于按范围将类型分组,用“ / ”指示可接受全部类型,用“ type/* ”指示可接受 type 类型的所有子类型
Accept-Language 客户端可接受的自然语言
Accept-Encoding 客户端可接受的编码压缩格式
Accept-Charset 可接受的应答的字符集
Host 请求的主机名,允许多个域名同处一个IP 地址,即虚拟主机
connection 连接方式(close或keepalive)
Cookie 存储于客户端扩展字段,向同一域名的服务端发送属于该域的cookie

空行

  • 最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。

请求包体

  • 请求包体不在GET方法中使用,而在POST方法中使用。POST方法适用于需要客户填写表单的场合。与请求包体相关的最常使用的是包体类型Content-Type和包体长度Content-Length。

推荐学习地址

  • https://segmentfault.com/a/1190000018129846#articleHeader0 -> Get/Post区别

  • https://github.com/guyan0319/golang_development_notes/blob/master/zh/7.2.md -> net/http源码分析

HTTP 服务端

Web服务器的工作原理

  • 客户机通过TCP/IP协议建立到服务器的TCP连接

  • 客户端向服务器发送HTTP协议请求包,请求服务器里的资源文档

  • 服务器向客户机发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理“动态内容”,并将处理得到的数据返回给客户端

  • 客户机与服务器断开。由客户端解释HTML文档,在客户端屏幕上渲染图形结果

Go中的web

  • Request:用户请求的信息,用来解析用户的请求信息,包括post、get、cookie、url等信息

  • Response:服务器需要反馈给客户端的信息

  • Conn:用户的每次请求链接

  • Handler:处理请求和生成返回信息的处理逻辑

  • 理解go中的http服务,最重要的就是要理解Multiplexer和hander,Golang中的Multiplexer基于ServerMux结构,同时也实现了Handler接口

  • 接收request的过程中,最重要的莫过于路由(router),即实现一个Multiplexer器。Go中既可以使用内置的mutilplexer–DefaultServeMux,也可以自定义。Multiplexer路由的目的就是为了找到处理器函数(hander),后者将对request进行处理,同时构建response。

回调函数

  • 本质:函数指针。通过地址,在某一特定位置,调用函数。

  • 在程序中,定义一个函数,但不显示调用。当某一条件满足时,该函数由操作系统自动调用。

实现简单的http服务

func sayHello(w http.ResponseWriter,r *http.Request) {
    fmt.Println("________________")
    r.ParseForm()   //解析参数,默认不会给解析
    fmt.Println("path ->",r.URL.Path)
    fmt.Println("scheme ->",r.URL.Scheme)
    fmt.Println(r.Form["url_long"])
    for k,v := range r.Form {
        fmt.Println("key ->",k)
        fmt.Println("value ->",v)
    }
    fmt.Fprintf(w,"Hello My Web")
}

func main() {
    //参数一:pattern string 监听的路径
    //参数二:handler func(ResponseWriter,*Request)
    http.HandleFunc("/",sayHello)   //设置访问的路由
    /*
    参数一:addr 监听地址
    参数二:handler 通常为空,意味服务端调用http.DefaultServerMux进行处理
    而服务端编写的业务逻辑处理程序http.Handle()或http.HandleFunc()
    默认注入http.DefaultServeMux中
    */
    err:=http.ListenAndServe(":8000",nil)
    if err != nil {
        log.Fatal("ListenAndServer:",err)
    }
}

http-go源码实现

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    if fn := testHookServerServe; fn != nil {
        fn(srv, l)
    }
    var tempDelay time.Duration // how long to sleep on accept failure

    if err := srv.setupHTTP2_Serve(); err != nil {
        return err
    }

    srv.trackListener(l, true)
    defer srv.trackListener(l, false)

    baseCtx := context.Background() // base is always background, per Issue 16220
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    for {
        rw, e := l.Accept()
        if e != nil {
            select {
            case <-srv.getDoneChan():
                return ErrServerClosed
            default:
            }
            if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay *= 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }
}

HTTP 客户端

//解析天气的例子

func main() {
    Url := "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=58367"
    ResPonse,_ := http.Get(Url)
    defer ResPonse.Body.Close()
    if ResPonse.StatusCode == 200 {
        body,_ := ioutil.ReadAll(ResPonse.Body)
        fmt.Println(string(body))
    }
    fmt.Println("___________天气预报_________")
    fmt.Printf("ResPonse: %v\n",ResPonse)
    fmt.Println("-----------------------------------------")
    fmt.Printf("response.Body:%+v\n", ResPonse.Body)
    fmt.Printf("response.Header:%+v\n", ResPonse.Header)
    fmt.Printf("response.StatusCode:%+v\n", ResPonse.StatusCode)
    fmt.Printf("response.Status:%+v\n", ResPonse.Status)
    fmt.Printf("response.Request:%+v\n", ResPonse.Request)
    fmt.Printf("response.Cookies:%+v\n", ResPonse.Cookies())
}
结果输出:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://WebXml.com.cn/">
  <string>直辖市</string>
  <string>上海</string>
  <string>58367</string>
  <string>58367.jpg</string>
  <string>2019/3/13 14:19:03</string>
  <string>9℃/16℃</string>
  <string>3月13日 多云转小雨</string>
  <string>东南风3-4级</string>
  <string>1.gif</string>
  <string>7.gif</string>
  <string>今日天气实况:气温:15℃;风向/风力:西北风 1级;湿度:33%;紫外线强度:最弱。空气质量:良。</string>
  <string>紫外线指数:最弱,辐射弱,涂擦SPF8-12防晒护肤品。
健臻·血糖指数:不易波动,天气条件好,血糖不易波动,可适时进行户外锻炼。
穿衣指数:较冷,建议着厚外套加毛衣等服装。
洗车指数:不宜,有雨,雨水和泥水会弄脏爱车。
空气污染指数:良,气象条件有利于空气污染物扩散。
</string>
  <string>7℃/13℃</string>
  <string>3月14日 小雨转晴</string>
  <string>南风3-4级</string>
  <string>7.gif</string>
  <string>0.gif</string>
  <string>7℃/18℃</string>
  <string>3月15日 多云</string>
  <string>南风3-4级转小于3级</string>
  <string>1.gif</string>
  <string>1.gif</string>
  <string>上海简称:沪,位置:上海地处长江三角洲前缘,东濒东海,南临杭州湾,西接江苏,浙江两省,北界长江入海,正当我国南北岸线的中部,北纬31°14′,东经121°29′。面积:总面积7823.5平方公里。人口:人口1000多万。上海丰富的人文资源、迷人的城市风貌、繁华的商业街市和欢乐的节庆活动形成了独特的都市景观。游览上海,不仅能体验到大都市中西合壁、商儒交融、八方来风的氛围,而且能感受到这个城市人流熙攘、车水马龙、灯火璀璨的活力。上海在中国现代史上占有着十分重要的地位,她是中国共产党的诞生地。许多震动中外的历史事件在这里发生,留下了众多的革命遗迹,处处为您讲述着一个个使人永不忘怀的可歌可泣的故事,成为包含民俗的人文景观和纪念地。在上海,每到秋祭,纷至沓来的人们在这里祭祀先烈、缅怀革命历史,已成为了一种风俗。大上海在中国近代历史中,曾是风起云涌可歌可泣的地方。在这里荟萃多少风云人物,散落在上海各处的不同住宅建筑,由于其主人的非同寻常,蕴含了耐人寻味的历史意义。这里曾留下许多革命先烈的足迹。瞻仰孙中山、宋庆龄、鲁迅等故居,会使您产生抚今追昔的深沉遐思,这里还有无数个达官贵人的住宅,探访一下李鸿章、蒋介石等人的公馆,可以联想起主人那段显赫的发迹史。</string>
</ArrayOfString>
___________天气预报_________
ResPonse: &{200 OK 200 HTTP/1.1 1 1 map[Cache-Control:[private, max-age=0] X-Powered-By:[ASP.NET] Date:[Wed, 13 Mar 2019 06:29:31 GMT] Content-Type:[text/xml; charset=utf-8] Vary:[Accept-Encoding] Server:[Microsoft-IIS/7.5] X-Aspnet-Version:[2.0.50727]] 0xc0420ee080 -1 [] false true map[] 0xc0420d8000 <nil>}
-----------------------------------------
response.Body:&{body:0xc0420e8140 zr:0xc0420f82c0 zerr:<nil>}
response.Header:map[X-Aspnet-Version:[2.0.50727] Content-Type:[text/xml; charset=utf-8] Vary:[Accept-Encoding] Server:[Microsoft-IIS/7.5] Date:[Wed, 13 Mar 2019 06:29:31 GMT] Cache-Control:[private, max-age=0] X-Powered-By:[ASP.NET]]
response.StatusCode:200
response.Status:200 OK
response.Request:&{Method:GET URL:http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=58367 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[] Body:<nil> GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:www.webxml.com.cn Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr: RequestURI: TLS:<nil> Cancel:<nil> Response:<nil> ctx:<nil>}
response.Cookies:[]

实现简单的文件服务器

package main

import (
    "log"
    "net/http"
)

func main() {
    err := http.ListenAndServe(":5000",http.FileServer(http.Dir("C://")))
    if err != nil {
        log.Fatal(err)
    }
}

实战云盘项目

目录结构

data
└── go
    └── work
        ├── bin
        ├── pkg
        └── src
            ├── filestore_server
            │   ├── db
            │   │   ├── dbfile.go
            │   │   ├── mysql
            │   │   │   └── db.go
            │   │   ├── userfile.go
            │   │   ├── userfileupload.go
            │   │   └── user.go
            │   ├── handler
            │   │   ├── handler.go
            │   │   └── user.go
            │   ├── main.go
            │   ├── meta
            │   │   └── meta.go
            │   ├── static
            │   │   ├── img
            │   │   │   └── avatar.jpeg
            │   │   ├── js
            │   │   │   └── auth.js
            │   │   └── view
            │   │       ├── home.html
            │   │       ├── index.html
            │   │       ├── signin.html
            │   │       └── signup.html
            │   └── util
            │       ├── resp.go
            │       └── util.go
            ├── github.com
            └── golang.org

项目API规划

接口描述 接口Url
文件上传接口 Post /file/upload
文件查询接口 Get /file/query
文件下载接口 Get /file/download
文件删除接口 POST /file/delete
文件修改(重命名) POST /file/update
用户注册 POST /user/signup
用户登入 POST /user/signin
用户信息 POST /user/info

文件的校验值计算

校验算法类型              各方面的比较
CRC(32/64)              1.校验值长度
                        2.校验值类别
MD5                     3.安全级别
                        4.计算效率
SHA1                    5.应用场景

CRC  ->  文件传输情况,传输的两端都使用CRC进行检查检验,保证传输过程中文件没有被篡改
MD5  ->  常用文件签名
SHA1 ->  文件唯一的标志

文件秒传的原理

  • 1.用户上传 -> 有相同的文件已经被上传则不需要二次上传直接可以使用覆盖
  • 2.离线下载 -> 大文件秒下载
  • 3.好友分享

关键点

  • 文件HASH(MD5,SHA1)的计算执行成功

  • 用户文件关联(文件的隔离可以给多个用户使用)

蹒跚学Go-Http编程+爬虫》有10个想法

  1. Outstanding post, you have pointed out some fantastic points, I as well think this is a very great website. fafkakgdgbag

  2. Usually I do not read writeup on blogs, nevertheless I wish to say that this writeup extremely forced me to take a look at and do so! Your writing taste has been amazed me. Thanks, really wonderful post. ddkdgdcefgdcdecc

  3. I discovered your blog site on google and test a number of of your early posts. Proceed to keep up the excellent operate. I simply extra up your RSS feed to my MSN Information Reader. Looking for forward to reading extra from you in a while! ddkdkdaekekf

  4. whoah this weblog is wonderful i really like studying your posts. Keep up the great paintings! You already know, lots of individuals are searching around for this information, you can help them greatly. bkaceecedckdbkcf

  5. I like what you guys are up also. Such smart work and reporting! Carry on the excellent works guys I’ve incorporated you guys to my blogroll. I think it’ll improve the value of my website 🙂

发表评论

电子邮件地址不会被公开。 必填项已用*标注