Golang网络编程-HTTP编程实战篇

          Golang网络编程-HTTP编程实战篇

                               作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

 

 

 

 

一.HTTP概述

1>.HTTP概述

  一个Web服务器也被称为HTTP服务器,它通过HTTP (HyperText Transfer Protocol 超文本传输协议)协议与客户端通信。这个客户端通常指的是Web浏览器(其实手机端客户端内部也是浏览器实现的)。

  Web服务器的工作原理可以简单地归纳为:
    1>.客户机通过TCP/IP协议建立到服务器的TCP连接
    2>.客户端向服务器发送HTTP协议请求包,请求服务器里的资源文档
    3>.服务器向客户机发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理“动态内容”,并将处理得到的数据返回给客户端
    4>.客户机与服务器断开。由客户端解释HTML文档,在客户端屏幕上渲染图形结果

2>.HTTP协议

  超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,它详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议。

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

3>.HTTP请求报文格式说明

  如上图所示,HTTP 请求报文由请求行、请求头部、空行、请求包体4个部分组成。

  请求行
    请求行由方法字段、URL字段 和HTTP 协议版本字段 3个部分组成,他们之间使用空格隔开。常用的 HTTP 请求方法有GET、POST。
      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。

4>.HTTP响应报文说明

  如上图所示,HTTP 响应报文由状态行、响应头部、空行、响应包体4个部分组成。

  状态行
    状态行由 HTTP 协议版本字段、状态码和状态码的描述文本3个部分组成,他们之间使用空格隔开。
    状态码:状态码由三位数字组成,第一位数字表示响应的类型,常用的状态码有五大类如下所示:
      1xx:    
        表示服务器已接收了客户端请求,客户端可继续发送请求
      2xx:
        表示服务器已成功接收到请求并进行处理
      3xx:
        表示服务器要求客户端重定向
      4xx:
        表示客户端的请求有非法内容
      5xx:
        表示服务器未能正常处理客户端的请求而出现意外错误
    常见的状态码举例:
      200:
        表示OK,即客户端请求成功
      400:
        表示Bad Request,即请求报文有语法错误
      401:
        表示Unauthorized,即未授权
      403:
        表示Forbidden,即服务器拒绝服务
      404:
        表示Not Found,即请求的资源不存在
      500:
        表示Internal Server Error,即服务器内部错误
      503:
        表示Server Unavailable,即服务器临时不能处理客户端请求(稍后可能可以)

  响应头部
    响应头可能包括:
      Location:
        响应报头域用于重定向接受者到一个新的位置
      Server:
        响应报头域包含了服务器用来处理请求的软件信息及其版本
      Vary:
        指示不可缓存的请求头列表
      Connection:
        连接方式

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

  响应包体
    服务器返回给客户端的文本信息。

5>.博主推荐阅读

  http协议版本,工作机制及http服务器应用扫盲篇:
    https://www.cnblogs.com/yinzhengjie/p/11986869.html

  HTTP协议详解:
    https://www.cnblogs.com/yinzhengjie/p/12014076.html

  前后端分离-Restful最佳实践
    https://www.cnblogs.com/yinzhengjie/p/12037939.html

二.使用Golang编写一个简单的Web服务器(生产环境建议初学者使用开源的web框架,比如Beego,Gin等)

package main

import (
    "fmt"
    "net/http"
)

func UserResp(resp http.ResponseWriter, req *http.Request) {
    fmt.Printf("请求方法: %s
", req.Method)
    fmt.Printf("浏览器发送请求文件路径: %s
", req.URL)
    fmt.Printf("请求头: %s
", req.Header)
    fmt.Printf("请求包体: %s
", req.Body)
    fmt.Printf("客户端网络地址: %s
", req.RemoteAddr)
    fmt.Printf("客户端Agent: %s
", req.UserAgent())

    /**
    给客户端回复数据
    */
    resp.Write([]byte("User response"))
}

func IndexResp(resp http.ResponseWriter, req *http.Request) {
    resp.Write([]byte("Index response"))
}

func main() {
    /**
    为不同的请求注册不同的函数
    */
    http.HandleFunc("/user", UserResp)
    http.HandleFunc("/index", IndexResp)

    //开启服务器,监听客户端的请求
    http.ListenAndServe("127.0.0.1:8080", nil)

}

三.使用Golang发起HTTP请求

package main

import (
    "fmt"
    "net/http"
)

func main() {

    /**
    该URL是咱们自己刚刚编写简单的Web服务器对应的资源。
    */
    url := "http://127.0.0.1:8080/user"

    resp, err := http.Get(url)
    if err != nil {
        fmt.Println("获取数据失败,错误原因: ", err)
        return
    }
    defer resp.Body.Close()

    /**
    获取从服务器端读到数据
    */
    fmt.Printf("状态: %s
", resp.Status)
    fmt.Printf("状态码: %v
", resp.StatusCode)
    fmt.Printf("响应头部: %s
", resp.Header)
    fmt.Println("响应包体: ", resp.Body)

    /**
    定义切片缓冲区,临时存储读到的数据,并将每次读到的结果拼接到data中
    */
    buf := make([]byte, 4096)
    var data string

    for {
        n, _ := resp.Body.Read(buf)
        if n == 0 {
            fmt.Println("数据读取完毕....")
            break
        }
        if err != nil {
            fmt.Println("数据读取失败,错误原因: ", err)
            return
        }
        data += string(buf[:n])
    }

    fmt.Printf("从服务端获取到的内容是: [%s]
", data)
}

原文地址:https://www.cnblogs.com/yinzhengjie2020/p/12723694.html