HTTP协议
HTTP协议是超文本传输协议,工作在应用层,它是基于TCP/IP通信协议来传输数据。
HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。
主要特点有:
- 1、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
- 2、灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
- 3.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 4.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
- 5、支持B/S及C/S模式。
对其实现主要是客户端和服务端得实现,在Go语言中使用net/http
包来实现服务端和客户端。
HTTP服务端
服务端的处理流程与Socket一样:
- 监听
- 等待请求
- 处理请求
默认Server
ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端。处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。
Handle和HandleFunc函数可以向DefaultServeMux添加处理器。http.Handle("/foo", fooHandler)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080", nil))
示例:
package main
import (
"fmt"
"net/http"
)
func helloWorld(response http.ResponseWriter, request *http.Request) {
fmt.Println("Hello World")
response.Write([]byte("你好"))
}
func main() {
// 接受请求并处理
http.HandleFunc("/", helloWorld)
// 监听
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Println("http server listen failed. err", err)
return
}
}
然后启动服务,在浏览器访问:
HTTP客户端
客户端主要是发一些GET
、POST
请求等,在Go语言中使用Get
、Head
、Post
、PostForm
函数来发送HTTP/HTTPS请求。
resp, err := http.Get("http://example.com/")
...
resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
...
resp, err := http.PostForm("http://example.com/form",
url.Values{"key": {"Value"}, "id": {"123"}})
程序在使用完response后必须关闭回复的主体。
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// ...
Get请求
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main(){
resp,err:=http.Get("https://www.baidu.com")
if err != nil {
fmt.Println("请求失败. err:",err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status)
// 读取王内容
content,err:=ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取文件内容失败. err",err)
return
}
fmt.Println(string(content))
}
带参数的Get请求
用上面的方法也可以带参数,如下:
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main(){
resp,err:=http.Get("https://baike.baidu.com/item/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884?fr=aladdin")
if err != nil {
fmt.Println("请求失败. err:",err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status)
// 读取王内容
content,err:=ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取文件内容失败. err",err)
return
}
fmt.Println(string(content))
}
但是这种很多就不灵活。Go语言中net/url
包可以用来处理加参数的请求。
如下:
Client端 :
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
func main() {
// 请求的url
reqURL := "http://127.0.0.1:9000/get"
// 构造请求数据
data := url.Values{}
// 向里面放入值
data.Set("name", "joker")
data.Set("age", "20")
// 解析上面的url
u, err := url.ParseRequestURI(reqURL)
if err != nil {
fmt.Println("url解析失败. err:", err)
return
}
// 对需要传输的数据进行编码
u.RawQuery = data.Encode()
fmt.Println(u.String())
// 发送请求
resp, err := http.Get(u.String())
if err != nil {
fmt.Println("请求失败. err:", err)
return
}
fmt.Println(resp.Status)
defer resp.Body.Close()
// 读取请求到的内容
bn, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取内容失败。err:", err)
return
}
// 输出内容
fmt.Println(string(bn))
}
Server端 :
package main
import (
"fmt"
"net/http"
)
func helloWorld(response http.ResponseWriter, request *http.Request) {
fmt.Println("Hello World")
response.Write([]byte("你好"))
}
func getTest(response http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
fmt.Println(request.URL.Query())
answer := []byte("ok")
response.Write(answer)
}
func main() {
// 接受请求并处理
http.HandleFunc("/", helloWorld)
http.HandleFunc("/get", getTest)
// 监听
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Println("http server listen failed. err", err)
return
}
}
POST请求
Client端 :
package main
import (
"fmt"
"net/http"
"strings"
)
func main() {
// post请求url
postURL := "http://127.0.0.1:9000/post"
// 定义文件类型
contentType := "application/json"
// 定义post的数据
data := `{"name":"joker","age":20}`
// 发送post请求
resp, err := http.Post(postURL, contentType, strings.NewReader(data))
if err != nil {
fmt.Println("post请求失败。err:", err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status)
}
Server端 :
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func helloWorld(response http.ResponseWriter, request *http.Request) {
fmt.Println("Hello World")
response.Write([]byte("你好"))
}
func getTest(response http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
fmt.Println(request.URL.Query())
answer := []byte("ok")
response.Write(answer)
}
func postTest(response http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
// 解析form数据
request.ParseForm()
fmt.Println(request.ParseForm())
fmt.Println(request.PostForm.Get("name"), request.PostForm.Get("age"))
b, err := ioutil.ReadAll(request.Body)
if err != nil {
fmt.Println("数据读取失败。err:", err)
return
}
fmt.Println(string(b))
}
func main() {
// 接受请求并处理
http.HandleFunc("/", helloWorld)
http.HandleFunc("/get", getTest)
http.HandleFunc("/post", postTest)
// 监听
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Println("http server listen failed. err", err)
return
}
}
自定义Client
要管理HTTP客户端的头域、重定向策略和其他设置,创建一个Client:
package main
import (
"net/http"
"strings"
"fmt"
"io/ioutil"
"log"
"encoding/json"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("POST", "http://www.maimaiche.com/loginRegister/login.do",
strings.NewReader("mobile=xxxxxxxxx&isRemberPwd=1"))
if err != nil {
log.Println(err)
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
resp, err := client.Do(req)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err)
return
}
fmt.Println(resp.Header.Get("Content-Type")) //application/json;charset=UTF-8
type Result struct {
Msg string
Status string
Obj string
}
result := &Result{}
json.Unmarshal(body, result) //解析json字符串
if result.Status == "1" {
fmt.Println(result.Msg)
} else {
fmt.Println("login error")
}
fmt.Println(result)
}
自定义Transport
要管理代理、TLS配置、keep-alive、压缩和其他设置,创建一个Transport:
tr := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool},
DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")
Client和Transport类型都可以安全的被多个goroutine同时使用。出于效率考虑,应该一次建立、尽量重用。