1、网络基础

基本TCP客户-服务器程序Socket编程流程如如下图所示。

TCP服务器绑定到特定端口并阻塞监听客户端端连接,

TCP客户端则通过IP+端口向服务器发起请求,客户-服务器建立连接之后就能开始进行数据传输。

Golang的TCP编程也是基于上述流程的。

2、Golang HTTP编程

2.1 代码示例

func timeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%v", time.Now().Format(time.RFC3339))
} func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%v", "hello world.")
} func main() {
// 1. 新建路由解码器
h := http.NewServeMux()
// 2. 路由注册
h.HandleFunc("/hello", helloHandler)
h.HandleFunc("/time", timeHandler)
// 3. 服务启动 阻塞监听
http.ListenAndServe(":8000", h)
}

运行上述程序,在浏览器地址栏分别输入 http://localhost:8000/hello http://localhost:8000/time 结果分别如下图所示。

 

2.2 源码分析

分析从路由注册到响应用户请求的流程。

2.2.1 新建解码器 h := http.NewServeMux()

type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
type muxEntry struct {
h Handler
pattern string
}
// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { return new(ServeMux) }

Handler是interface,定义如下

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

ServeMux实现了Handler接口。

2.2.2 路由注册 h.HandleFunc("/hello", helloHandler)

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
...
mux.Handle(pattern, HandlerFunc(handler))
} func (mux *ServeMux) Handle(pattern string, handler Handler) {
...
e := muxEntry{h: handler, pattern: pattern}
mux.m[pattern] = e
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
...
}

timeHandlerhelloHandler函数被强制转换为type HandlerFunc func(ResponseWriter, *Request)类型,且实现了Handler接口。

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}

mux.m建立了路由到处理函数timeHandlerhelloHandler的映射。

2.2.3 服务启动阻塞监听 http.ListenAndServe(":8000", h)

包装Server结构体,HTTP使用TCP协议。

func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
func (srv *Server) ListenAndServe() error {
...
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(ln)
}

net.Listen封装了Socket编程的socketbindlisten的调用,极大的方便了使用者。

阻塞监听请求,新建goroutine处理每个新请求。

func (srv *Server) Serve(l net.Listener) error {
...
for {
rw, err := l.Accept()
...
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
}
}
// Serve a new connection.
func (c *conn) serve(ctx context.Context) {
...
serverHandler{c.server}.ServeHTTP(w, w.req)
...
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
...
handler.ServeHTTP(rw, req)
}

通过前面的流程推导可知,handler是http.ListenAndServe的第二个参数ServeMux

// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
...
h, _ := mux.Handler(r) // 通过路由获取处理函数
h.ServeHTTP(w, r)
}

mux.Handler使用mux.m这个map通过请求URL找到对应处理函数的。

h的实际类型为HandlerFunc,根据2.2.2会调用到具体函数timeHandler或者helloHandler

3. 总结

golang对socket编程进行了封装,给HTTP编程带来了极大的便利。

但是不支持一下特性

1. 路由分组 对路由进行分组,可以方便分组鉴权

2. 动态路由 如动态路由/user/:username/post/:postid不支持

Golang HTTP编程及源码解析的更多相关文章

  1. golang的flag包源码解析与使用

    当我们 import  package时,package内的全局常量和全局变量会进行初始化,并且紧接着init函数会执行.因此我们先看一下flag包的全局常量和全局变量. 一.flag包的全局常量.全 ...

  2. 【Java源码解析】Thread

    简介 线程本质上也是进程.线程机制提供了在同一程序内共享内存地址空间运行的一组线程.对于内核来讲,它就是进程,只是该进程和其他一下进程共享某些资源,比如地址空间.在Java语言里,Thread类封装了 ...

  3. MyBatis源码解析【7】接口式编程

    前言 这个分类比较连续,如果这里看不懂,或者第一次看,请回顾之前的博客 http://www.cnblogs.com/linkstar/category/1027239.html 修改例子 在我们实际 ...

  4. 【Android编程】android平台的MITM瑞士军刀_cSploit源码解析及中间人攻击复现

    /文章作者:Kali_MG1937 作者博客ID:ALDYS4 QQ:3496925334 未经允许,禁止转载/ 何为MITM欺骗,顾名思义,中间人攻击的含义即为在局域网中充当数据包交换中间人的角色 ...

  5. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  6. 源码解析-Volley(转自codeKK)

    Volley 源码解析 本文为 Android 开源项目源码解析 中 Volley 部分项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo分析者:grumoon ...

  7. jQuery整体架构源码解析(转载)

    jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ...

  8. String,StringBuffer和StringBuilder源码解析[基于JDK6]

    最近指导几位新人,学习了一下String,StringBuffer和StringBuilder类,从反馈的结果来看,总体感觉学习的深度不够,没有读出东西.其实,JDK的源码是越读越有味的.下面总结一下 ...

  9. kubernetes源码解析---- apiserver路由构建解析(1)

    kubernetes源码解析---- apiserver路由构建解析(1) apiserver作为k8s集群的唯一入口,内部主要实现了两个功能,一个是请求的路由和处理,简单说就是监听一个端口,把接收到 ...

  10. Retrofit2 源码解析

    原文链接:http://bxbxbai.github.io/2015/12/13/retrofit2-analysis/ 公司里最近做的项目中网络框架用的就是Retrofit,用的多了以后觉得这个框架 ...

随机推荐

  1. MySQL库,表,数据的操作

    数据库的操作 1. 创建数据库 create database [if not exists] `数据库名` charset=字符编码(utf8mb4); 如果多次创建会报错 如果不指定字符编码,默认 ...

  2. 基于opencv实现简单人脸检测

    作用:在视频中自动检测出人脸 使用内容:灰度转换,分类器,矩形框选,圆形框选,摄像头读取及释放 代码逻辑: 先读取摄像头后, 对摄像头拍摄到的图片进行逐帧分析, 并对图像进行灰度转换后使用类选择器识别 ...

  3. aiohttp、asyncio使用协程增加爬虫效率

    import aiohttp import asyncio import time async def get_requests(url): async with aiohttp.ClientSess ...

  4. Linux 常用命令(持续更新)

    Linux常用命令介绍(备查) *所有的命名都可以用 命令 --help/man 命令 查看使用说明 1.pwd 显示当前路径 2.dir 和 ls用法一样 都是列出当前路径下的文件(不包括隐藏文件) ...

  5. SQL注入问题、视图、触发器、事物、存储过程、函数、流程控制、索引相关概念、索引数据结构、慢查询优化、

    目录 SQL注入问题 视图 触发器 事物 存储过程 函数 流程控制 索引相关概念 索引数据结构 慢查询优化 测试装备 联合索引 全文检索 插入数据 更新数据 删除数据 主键 外键 重命名表 事物 安全 ...

  6. 从面试题入手,畅谈 Vue 3 性能优化

    前言 今年又是一个非常寒冷的冬天,很多公司都开始人员精简.市场从来不缺前端,但对高级前端的需求还是特别强烈的.一些大厂的面试官为了区分候选人对前端领域能力的深度,经常会在面试过程中考察一些前端框架的源 ...

  7. (已转)C++知识图谱

  8. DevExpress 的LayoutControl控件导致资源无法释放的问题处理

    现象记录 前段时间同事发现我们的软件在加载指定的插件界面后,关闭后插件的界面资源不能释放, 资源管理器中不管内存,还是GDI对象等相关资源都不会下降. 问题代码 问题的代码大概如下. public v ...

  9. AcWing第85场周赛

    这场周赛是手速局hh 死或生 某国正在以投票的方式决定 2 名死刑犯(编号 1∼2)的生死. 共有 n 组人员(编号 1∼n)参与投票,每组 10 人. 每组成员只参与一名死刑犯的投票,其中第 i 组 ...

  10. 聊聊Cookie、Session、Token 背后的故事

    摘要:Cookie.Session.Token 这三者是不同发展阶段的产物 本文分享自华为云社区<Cookie.Session.Token 背后的故事>,作者: 龙哥手记. 1. 网站交互 ...