网络编程【6】Golang实现HTTP 服务端和客户端

目录:
一、HTTP服务器:
	- 创建路由器
	- 设置路由规则
	- 创建服务器
	- 监听端口并提供服务
二、HTTP客户端:
	- 创建连接池
	- 创建客户端
	- 请求资源(简单请求、携带header请求、携带body请求)
	- 读取内容
三、http服务器源码分析:
	- mux为何能作为http.Server的Handler参数?
	- 自定义路由器
三、http客户端transport分析:
	- http超时控制
	- RoundTrip流程

一、HTTP服务器

var addr string = "172.?.?.249:8888"

func update(rw http.ResponseWriter, req *http.Request) {
	// 1. 如何body不是json数据结构
	if !strings.Contains(req.Header.Get("Content-Type"), "json") {
		rw.Write([]byte("无效的数据格式"))
		return
	}
	// 2. 处理json字符串
	var accept = make(map[string]interface{}, 5)
	bytes, _ := ioutil.ReadAll(req.Body)
	json.Unmarshal(bytes, &accept)
	rw.Write([]byte(fmt.Sprintf("update session: %v", accept["session"])))
}

func hello(rw http.ResponseWriter, req *http.Request) {
	rw.Write([]byte("welcome"))
}

func main() {
	// 1. 创建路由器
	mux := http.NewServeMux()
	// 2. 设置路由规则
	mux.HandleFunc("/hello", hello)
	mux.HandleFunc("/update", update)
	// 3. 创建服务器
	server := &http.Server{
		Addr:         addr,
		WriteTimeout: time.Second * 3,
		Handler:      mux,
	}
	log.Printf("listen on %v\n", addr)
	// 4. 监听端口并提供服务
	log.Fatal(server.ListenAndServe())
}

二、HTTP客户端

func main() {
	// 1. 创建连接池
	transport := &http.Transport{
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second, // 连接超时
			KeepAlive: 30 * time.Second, // 长连接超时
		}).DialContext,
		MaxIdleConns:          100,              // 最大空闲连接
		IdleConnTimeout:       90 * time.Second, // 空闲连接超时
		TLSHandshakeTimeout:   10 * time.Second, // tls握手时间
		ExpectContinueTimeout: 1 * time.Second,  // 100-continue状态码超时时间
	}
	// 2. 创建客户端
	client := &http.Client{
		Timeout:   time.Second * 30, // 请求超时时间
		Transport: transport,
	}

	// 3. 普通请求
	resp1, err := client.Get("http://172.?.?.249:8888/hello")
	if err != nil {
		log.Fatal(err)
	}
	defer resp1.Body.Close()
	bytes1, _ := ioutil.ReadAll(resp1.Body)
	fmt.Println("simple request:", string(bytes1))

	// 4. 复杂请求(携带header和body)
	req, _ := http.NewRequest("GET", "http://172.?.?.249:8888/update", strings.NewReader(`{"session": "9527"}`))
	req.Header.Add("Content-Type", "application/json;charset=utf-8")
	resp2, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp2.Body.Close()
	bytes, _ := ioutil.ReadAll(resp2.Body)
	fmt.Println(string(bytes))
}

三、http服务器源码分析
3.1 mux为何能作为http.Server的Handler参数?
先看http.Handler,是一个接口方法,实现该接口的所有方法即可套用

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

再看http.ServeMux代码,里面实现了一个ServerHTTP方法,所以http.ServeMux可以当做http.Handler参数

func (mux *ServeMux) match(path string) (h Handler, pattern string) {}
func (mux *ServeMux) redirectToPathSlash(host, path string, u *url.URL) (*url.URL, bool) {}
func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool {}
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {}
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {}
func (mux *ServeMux) Handle(pattern string, handler Handler) {}
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {}

3.2 自定义路由器
根据以上机制实现一个简单的路由器

  1. 实现ServerHTTP方法
  2. 将对象传入即可
var addr string = "172.?.?.249:8888"

type Handler func(http.ResponseWriter, *http.Request)

type MyMux struct {
	Func map[string]Handler
}
// SetFunc 设置路由函数
func (m *MyMux) SetFunc(uri string, handler func(rw http.ResponseWriter, req *http.Request)) {
	if m.Func == nil {
		m.Func = make(map[string]Handler, 5)
	}
	m.Func[uri] = handler
}

func (m *MyMux) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	for uri, def := range m.Func {
		if req.RequestURI == uri {
			def(rw, req)
			return
		}
	}
	notFound(rw, req)
}
func notFound(rw http.ResponseWriter, req *http.Request) {
	rw.Write([]byte("no found"))
}

func hello(rw http.ResponseWriter, req *http.Request) {
	rw.Write([]byte("welcome2"))
}

func main() {
	// 1. 创建路由器
	mymux := &MyMux{}
	// 2. 注册路由
	mymux.SetFunc("/hello", hello)
	// 3. 创建服务器
	server := &http.Server{
		Addr:         addr,
		WriteTimeout: time.Second * 3,
		Handler:      mymux,
	}
	log.Printf("listen on %v\n", addr)
	// 4. 监听端口并提供服务
	log.Fatal(server.ListenAndServe())
}

四、Transport讲解
4.1 HTTP超时控制
在这里插入图片描述

func main() {
	// 1. 创建连接池
	transport := &http.Transport{
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second, // 连接超时
			KeepAlive: 30 * time.Second, // 长连接超时
		}).DialContext,
		MaxIdleConns:          100,              // 最大空闲连接
		IdleConnTimeout:       90 * time.Second, // 空闲连接超时
		TLSHandshakeTimeout:   10 * time.Second, // tls握手时间
		ExpectContinueTimeout: 1 * time.Second,  // 100-continue状态码超时时间
	}
	// 2. 创建客户端
	client := &http.Client{
		Timeout:   time.Second * 30, // 请求超时时间
		Transport: transport,
	}

4.2 RoundTrip流程
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值