go语言之行--网络编程、http处理流程详情

前端之家收集整理的这篇文章主要介绍了go语言之行--网络编程、http处理流程详情前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

一、简介

go语言中的网络编程主要通过net包实现,net包提供了网络I/O接口,包括HTTP、TCP/IP、UDP、域名解析和Unix域socket等。和大多数语言一样go可以使用几行代码便可以启动一个服务器,但是得益于goroutine的配合go实现的服务器拥有强大并发处理能力。

二、socket编程

Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递。 我们知道网络 通信 都 是基于 ip+port 方能定位到目标的具体机器上的具体服务,操作系统有0-65535个端口,每个端口都可以独立对外提供服务,如果 把一个公司比做一台电脑 ,那公司的总机号码就相当于ip地址, 每个员工的分机号就相当于端口, 你想找公司某个人,必须 先打电话到总机,然后再转分机 。

go中socket编程实现起来非常方便,下面是处理流程

服务器端:

  1. 监听端口
  2. 接受客户端连接
  3. 创建goroutine处理连接

客户端:

  1. 建立连接
  2. 收发数据
  3. 关闭连接

服务端示例:

import (
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">net<span style="color: #800000;">"<span style="color: #000000;">
)

func handle(conn net.Conn) { <span style="color: #008000;">//<span style="color: #008000;">处理连接方法
defer conn.Close() <span style="color: #008000;">//<span style="color: #008000;">关闭连接
<span style="color: #0000ff;">for<span style="color: #000000;">{
buf := make([]<span style="color: #0000ff;">byte,<span style="color: #800080;">100<span style="color: #000000;">)
n,err := conn.Read(buf) <span style="color: #008000;">//<span style="color: #008000;">读取客户端数据
<span style="color: #0000ff;">if err!=<span style="color: #000000;">nil {
fmt.Println(err)
<span style="color: #0000ff;">return<span style="color: #000000;">

    }
    fmt.Printf(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;read data size %d msg:%s</span><span style="color: #800000;"&gt;"</span>,n,<span style="color: #0000ff;"&gt;string</span>(buf[<span style="color: #800080;"&gt;0</span><span style="color: #000000;"&gt;:n]))
    msg :</span>= []<span style="color: #0000ff;"&gt;byte</span>(<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;hello,world\n</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;)
    conn.Write(msg)  </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt;发送数据</span>

<span style="color: #000000;"> }
}
func main() {
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">start server....<span style="color: #800000;">"<span style="color: #000000;">)
listen,err := net.Listen(<span style="color: #800000;">"<span style="color: #800000;">tcp<span style="color: #800000;">",<span style="color: #800000;">"<span style="color: #800000;">0.0.0.0:3000<span style="color: #800000;">") <span style="color: #008000;">//<span style="color: #008000;">创建监听
<span style="color: #0000ff;">if err !=<span style="color: #000000;"> nil{
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">listen Failed! msg :<span style="color: #800000;">"<span style="color: #000000;">,err)
<span style="color: #0000ff;">return<span style="color: #000000;">
}
<span style="color: #0000ff;">for<span style="color: #000000;">{
conn,errs := listen.Accept() <span style="color: #008000;">//<span style="color: #008000;">接受客户端连接
<span style="color: #0000ff;">if errs !=<span style="color: #000000;"> nil{
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">accept Failed<span style="color: #800000;">"<span style="color: #000000;">)
<span style="color: #0000ff;">continue<span style="color: #000000;">
}
go handle(conn) <span style="color: #008000;">//<span style="color: #008000;">处理连接
<span style="color: #000000;"> }
}

客户端示例:

import (
<span style="color: #800000;">"<span style="color: #800000;">bufio<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">net<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">os<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">strings<span style="color: #800000;">"<span style="color: #000000;">
)

func main() {
conn,err := net.Dial(<span style="color: #800000;">"<span style="color: #800000;">tcp<span style="color: #800000;">",<span style="color: #800000;">"<span style="color: #800000;">127.0.0.1:3000<span style="color: #800000;">"<span style="color: #000000;">)
<span style="color: #0000ff;">if err !=<span style="color: #000000;"> nil {
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">err dialing:<span style="color: #800000;">"<span style="color: #000000;">,err.Error())
<span style="color: #0000ff;">return<span style="color: #000000;">
}
defer conn.Close()
inputReader :=<span style="color: #000000;"> bufio.NewReader(os.Stdin)
<span style="color: #0000ff;">for<span style="color: #000000;"> {
str,_ := inputReader.ReadString(<span style="color: #800000;">'<span style="color: #800000;">\n<span style="color: #800000;">'<span style="color: #000000;">)
data := strings.Trim(str,<span style="color: #800000;">"<span style="color: #800000;">\n<span style="color: #800000;">"<span style="color: #000000;">)
<span style="color: #0000ff;">if data == <span style="color: #800000;">"<span style="color: #800000;">quit<span style="color: #800000;">" { <span style="color: #008000;">//<span style="color: #008000;">输入quit退出
<span style="color: #0000ff;">return<span style="color: #000000;">
}
_,err := conn.Write([]<span style="color: #0000ff;">byte(data)) <span style="color: #008000;">//<span style="color: #008000;">发送数据
<span style="color: #0000ff;">if err !=<span style="color: #000000;"> nil {
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">send data error:<span style="color: #800000;">"<span style="color: #000000;">,err)
<span style="color: #0000ff;">return<span style="color: #000000;">
}
buf := make([]<span style="color: #0000ff;">byte,<span style="color: #800080;">512<span style="color: #000000;">)
n,err := conn.Read(buf) <span style="color: #008000;">//<span style="color: #008000;">读取服务端端数据
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">from server:<span style="color: #800000;">",<span style="color: #0000ff;">string<span style="color: #000000;">(buf[:n]))
}
}

conn示例还提供其他方法

type Conn Read(b []) (n </span></span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Write writes data to the connection. </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Write can be made to time out and return an Error with Timeout() == true </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; after a fixed time limit; see SetDeadline and SetWriteDeadline.</span> Write(b []<span style="color: #0000ff;"&gt;byte</span>) (n <span style="color: #0000ff;"&gt;int</span><span style="color: #000000;"&gt;,err error) <span style="color: #008000;"&gt;//发送数据 </span></span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Close closes the connection. </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Any blocked Read or Write operations will be unblocked and return errors.</span>

<span style="color: #000000;"> Close() error <span style="color: #008000;">//关闭链接

</span></span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; LocalAddr returns the local network address.</span>

<span style="color: #000000;"> LocalAddr() Addr<span style="color: #008000;"> //返回本地连接地址

</span></span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; RemoteAddr returns the remote network address.</span>

<span style="color: #000000;"> RemoteAddr() Addr <span style="color: #008000;">//返回远程连接的地址

</span></span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; SetDeadline sets the read and write deadlines associated
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; with the connection. It is equivalent to calling both
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; SetReadDeadline and SetWriteDeadline.
</span><span style="color: #008000;"&gt;//</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; A deadline is an absolute time after which I/O operations
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; fail with a timeout (see type Error) instead of
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; blocking. The deadline applies to all future and pending
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; I/O,not just the immediately following call to Read or
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Write. After a deadline has been exceeded,the connection
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; can be refreshed by setting a deadline in the future.
</span><span style="color: #008000;"&gt;//</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; An idle timeout can be implemented by repeatedly extending
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; the deadline after successful Read or Write calls.
</span><span style="color: #008000;"&gt;//</span>
<span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; A zero value for t means I/O operations will not time out.</span>

<span style="color: #000000;"> SetDeadline(t time.Time) error <span style="color: #008000;">//设置链接读取或者写超时时间

</span></span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; SetReadDeadline sets the deadline for future Read calls
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; and any currently-blocked Read call.
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; A zero value for t means Read will not time out.</span>

<span style="color: #000000;"> SetReadDeadline(t time.Time) error <span style="color: #008000;">//单独设置读取超时时间

</span></span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; SetWriteDeadline sets the deadline for future Write calls
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; and any currently-blocked Write call.
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Even if write times out,it may return n > 0,indicating that
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; some of the data was successfully written.
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; A zero value for t means Write will not time out.</span>

<span style="color: #000000;"> SetWriteDeadline(t time.Time) error<span style="color: #008000;">//单独设置写超时时间
}

三、go中HTTP服务处理流程

简介

网络发展,很多网络应用都是构建再 HTTP 服务基础之上。HTTP 协议从诞生到现在,发展从1.0,1.1到2.0也不断再进步。除去细节,理解 HTTP 构建的网络应用只要关注两个端---客户端(clinet)和服务端(server),两个端的交互来自 clinet 的 request,以及server端的response。所谓的http服务器,主要在于如何接受 clinet 的 request,并向client返回response。接收request的过程中,最重要的莫过于路由(router@H_301_285@),即实现一个Multiplexer@H_301_285@器。Go中既可以使用内置的mutilplexer --- DefautServeMux@H_301_285@,也可以自定义。Multiplexer路由的目的就是为了找到处理器函数handler@H_301_285@),后者将对request进行处理,同时构建response。

最后简化的请求处理流程为:

Clinet -> Requests -> [Multiplexer(router) -> handler -> Response -> Clinet

因此,理解go中的http服务,最重要就是要理解Multiplexer和handler,Golang中的Multiplexer基于ServeMux@H_301_285@结构,同时也实现了Handler@H_301_285@接口。

对象说明:

handler处理器和handler对象的差别在于,一个是函数,另外一个是结构,它们都有实现了ServeHTTP方法。很多情况下它们的功能类似,下文就使用统称为handler。

Handler

Golang没有继承,类多态的方式可以通过接口实现。所谓接口则是定义声明了函数签名,任何结构只要实现了与接口函数签名相同的方法,就等同于实现了接口。go的http服务都是基于handler进行处理。

type Handler *

任何结构体,只要实现了ServeHTTP方法,这个结构就可以称之为handler对象。ServeMux会使用handler并调用其ServeHTTP方法处理请求并返回响应。

ServeMux

源码部分:

type ServeMux type muxEntry <span style="color: #0000ff;">struct<span style="color: #000000;"> {
<span style="color: #0000ff;">explicit
<span style="color: #0000ff;">bool
<span style="color: #000000;">
h Handler
pattern
<span style="color: #0000ff;">string<span style="color: #000000;">
}

ServeMux结构中最重要的字段为m@H_301_285@,这是一个map,key是一些url模式,value是一个muxEntry结构,后者里定义存储了具体的url模式和handler。当然,所谓的ServeMux也实现了ServeHTTP接口,也算是一个handler,不过ServeMux的ServeHTTP方法不是用来处理request和respone,而是用来找到路由注册的handler,后面再做解释。

Server

除了ServeMux和Handler,还有一个结构Server需要了解。从http.ListenAndServe@H_301_285@的源码可以看出,它创建了一个server对象,并调用server对象的ListenAndServe方法

func ListenAndServe(addr = &

查看server的结构如下:

type Server *MaxHeaderBytes </span><span style="color: #0000ff;"&gt;int</span><span style="color: #000000;"&gt; TLSNextProto map[</span><span style="color: #0000ff;"&gt;string</span>]func(*Server,*<span style="color: #000000;"&gt;tls.Conn,Handler) ConnState func(net.Conn,ConnState) ErrorLog </span>*<span style="color: #000000;"&gt;log.Logger disableKeepAlives int32 nextProtoOnce sync.Once nextProtoErr error

}

server结构存储了服务器处理请求常见的字段。其中Handler字段也保留Handler接口。如果Server接口没有提供Handler结构对象,那么会使用DefautServeMux做multiplexer,后面再做分析。

创建HTTP服务

创建一个http服务,大致需要经历两个过程,首先需要注册路由,即提供url模式和handler函数的映射,其次就是实例化一个server对象,并开启对客户端的监听。

http.HandleFunc(= &server.ListenAndServe()

示例:

func Hello(w http.ResponseWriter,r *<span style="color: #000000;">http.Request) {
fmt.Println(
<span style="color: #800000;">"
<span style="color: #800000;">Hello World.
<span style="color: #800000;">"
<span style="color: #000000;">)
fmt.Fprintf(w,
<span style="color: #800000;">"
<span style="color: #800000;">Hello World.\n
<span style="color: #800000;">"<span style="color: #000000;">)
}

func main() {
http.HandleFunc(<span style="color: #800000;">"<span style="color: #800000;">/<span style="color: #800000;">"<span style="color: #000000;">,Hello)
err := http.ListenAndServe(<span style="color: #800000;">"<span style="color: #800000;">0.0.0.0:6000<span style="color: #800000;">"<span style="color: #000000;">,nil)
<span style="color: #0000ff;">if err !=<span style="color: #000000;"> nil {
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">http listen Failed.<span style="color: #800000;">"<span style="color: #000000;">)
}
}

<span style="color: #008000;">//<span style="color: #008000;">curl <span style="color: #008000; text-decoration: underline;">http://127.0.0.1<span style="color: #008000;">:6000
<span style="color: #008000;">//<span style="color: #008000;"> 结果:Hello World

路由注册

net/http包暴露的注册路由的api很简单,http.HandleFunc选取了DefaultServeMux作为multiplexer:

func HandleFunc(pattern ,handler func(ResponseWriter,*

DefaultServeMux是ServeMux的一个实例。当然http包也提供了NewServeMux方法创建一个ServeMux实例,默认则创建一个DefaultServeMux:

func NewServeMux() *ServeMux { <span style="color: #008000;">//<span style="color: #008000;"> DefaultServeMux is the default ServeMux used by Serve.
<span style="color: #0000ff;">var
DefaultServeMux = &<span style="color: #000000;">defaultServeMux

<span style="color: #0000ff;">var defaultServeMux ServeMux

DefaultServeMux的HandleFunc(pattern,handler)方法实际是定义在ServeMux下的:

func (mux *ServeMux) HandleFunc(pattern ,*

HandlerFunc是一个函数类型。同时实现了Handler接口的ServeHTTP方法。使用HandlerFunc类型包装一下路由定义的indexHandler函数,其目的就是为了让这个函数也实现ServeHTTP方法,即转变成一个handler处理器(函数)。

type HandlerFunc func(ResponseWriter,*<span style="color: #008000;">//<span style="color: #008000;"> ServeHTTP calls f(w,r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter,r *<span style="color: #000000;">Request) {
f(w,r)
}

我们最开始写的例子中http.HandleFunc("/",Indexhandler)这样 IndexHandler 函数也有了ServeHTTP方法。ServeMux的Handle方法,将会对pattern和handler函数做一个map映射:

func ListenAndServe(addr func (srv *= addr == = = net.Listen( err != srv.Serve(tcpKeepAliveListener{ln.(*

Server的ListenAndServe方法中,会初始化监听地址Addr,同时调用Listen方法设置监听。最后将监听的TCP对象传入Serve方法

func (srv * fn := testHookServerServe; fn != tempDelay time.Duration
<span style="color: #0000ff;"&gt;if</span> err := srv.setupHTTP2_Serve(); err !=<span style="color: #000000;"&gt; nil {
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; err
}

srv.trackListener(l,</span><span style="color: #0000ff;"&gt;true</span><span style="color: #000000;"&gt;)
defer srv.trackListener(l,</span><span style="color: #0000ff;"&gt;false</span><span style="color: #000000;"&gt;)

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

<span style="color: #000000;"> go c.serve(ctx)
}
}

监听开启之后,一旦客户端请求到底,go就开启一个协程处理请求,主要逻辑都在serve方法之中。

serve方法比较长,其主要职能就是,创建一个上下文对象,然后调用Listener的Accept方法用来 获取连接数据并使用newConn方法创建连接对象。最后使用goroutine协程的方式处理连接请求。因为每一个连接都开起了一个协程,请求的上下文都不同,同时又保证了go的高并发。serve也是一个长长的方法

方法

使用defer定义了函数退出时,连接关闭相关的处理。然后就是读取连接的网络数据,并处理读取完毕时候的状态。接下来就是调用serverHandler{c.server}.ServeHTTP(w,w.req)@H_301_285@方法处理请求了。最后就是请求处理完毕的逻辑。serverHandler是一个重要的结构,它近有一个字段,即Server结构,同时它也实现了Handler接口方法ServeHTTP,并在该接口方法中做了一个重要的事情,初始化multiplexer路由多路复用器。如果server对象没有指定Handler,则使用默认的DefaultServeMux作为路由Multiplexer。并调用初始化Handler的ServeHTTP方法

type serverHandler *func (sh serverHandler) ServeHTTP(rw ResponseWriter,req <span style="color: #000000;">Request) {
handler :
=<span style="color: #000000;"> sh.srv.Handler
<span style="color: #0000ff;">if
handler ==<span style="color: #000000;"> nil {
handler
=<span style="color: #000000;"> DefaultServeMux
}
<span style="color: #0000ff;">if
req.RequestURI == <span style="color: #800000;">"<span style="color: #800000;"><span style="color: #800000;">" && req.Method == <span style="color: #800000;">"<span style="color: #800000;">OPTIONS<span style="color: #800000;">"<span style="color: #000000;"> {
handler =<span style="color: #000000;"> globalOptionsHandler{}
}
handler.ServeHTTP(rw,req)
}

这里DefaultServeMux的ServeHTTP方法其实也是定义在ServeMux结构中的,相关代码如下:

func (mux *ServeMux) match(path ) (h Handler,pattern v,ok :=</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Check for longest valid match.</span> <span style="color: #0000ff;"&gt;var</span> n = <span style="color: #800080;"&gt;0</span> <span style="color: #0000ff;"&gt;for</span> k,v :=<span style="color: #000000;"&gt; range mux.m { </span><span style="color: #0000ff;"&gt;if</span> !<span style="color: #000000;"&gt;pathMatch(k,path) { </span><span style="color: #0000ff;"&gt;continue</span><span style="color: #000000;"&gt; } </span><span style="color: #0000ff;"&gt;if</span> h == nil || len(k) ><span style="color: #000000;"&gt; n { n </span>=<span style="color: #000000;"&gt; len(k) h </span>=<span style="color: #000000;"&gt; v.h pattern </span>=<span style="color: #000000;"&gt; v.pattern } } </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt;

}
func (mux ServeMux) Handler(r Request) (h Handler,pattern <span style="color: #0000ff;">string<span style="color: #000000;">) {

</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; CONNECT requests are not canonicalized.</span>
<span style="color: #0000ff;"&gt;if</span> r.Method == <span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;CONNECT</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt; {
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; mux.handler(r.Host,r.URL.Path)
}

</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; All other requests have any port stripped and path cleaned
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; before passing to mux.handler.</span>
host :=<span style="color: #000000;"&gt; stripHostPort(r.Host)
path :</span>=<span style="color: #000000;"&gt; cleanPath(r.URL.Path)
</span><span style="color: #0000ff;"&gt;if</span> path !=<span style="color: #000000;"&gt; r.URL.Path {
    _,pattern </span>=<span style="color: #000000;"&gt; mux.handler(host,path)
    url :</span>= *<span style="color: #000000;"&gt;r.URL
    url.Path </span>=<span style="color: #000000;"&gt; path
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; RedirectHandler(url.String(),StatusMovedPermanently),pattern
}

</span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; mux.handler(host,r.URL.Path)

}

<span style="color: #008000;">//<span style="color: #008000;"> handler is the main implementation of Handler.
<span style="color: #008000;">//<span style="color: #008000;"> The path is known to be in canonical form,except for CONNECT methods.
func (mux *ServeMux) handler(host,path <span style="color: #0000ff;">string) (h Handler,pattern <span style="color: #0000ff;">string<span style="color: #000000;">) {
mux.mu.RLock()
defer mux.mu.RUnlock()

</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; Host-specific pattern takes precedence over generic ones</span>
<span style="color: #0000ff;"&gt;if</span><span style="color: #000000;"&gt; mux.hosts {
    h,pattern </span>= mux.match(host +<span style="color: #000000;"&gt; path)
}
</span><span style="color: #0000ff;"&gt;if</span> h ==<span style="color: #000000;"&gt; nil {
    h,pattern </span>=<span style="color: #000000;"&gt; mux.match(path)
}
</span><span style="color: #0000ff;"&gt;if</span> h ==<span style="color: #000000;"&gt; nil {
    h,pattern </span>= NotFoundHandler(),<span style="color: #800000;"&gt;""</span><span style="color: #000000;"&gt;
}
</span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt;

}

<span style="color: #008000;">//<span style="color: #008000;"> ServeHTTP dispatches the request to the handler whose
<span style="color: #008000;">//<span style="color: #008000;"> pattern most closely matches the request URL.
func (mux ServeMux) ServeHTTP(w ResponseWriter,r <span style="color: #000000;">Request) {
<span style="color: #0000ff;">if r.RequestURI == <span style="color: #800000;">"<span style="color: #800000;">*<span style="color: #800000;">"<span style="color: #000000;"> {
<span style="color: #0000ff;">if r.ProtoAtLeast(<span style="color: #800080;">1,<span style="color: #800080;">1<span style="color: #000000;">) {
w.Header().Set(<span style="color: #800000;">"<span style="color: #800000;">Connection<span style="color: #800000;">",<span style="color: #800000;">"<span style="color: #800000;">close<span style="color: #800000;">"<span style="color: #000000;">)
}
w.WriteHeader(StatusBadRequest)
<span style="color: #0000ff;">return<span style="color: #000000;">
}
h,_ :=<span style="color: #000000;"> mux.Handler(r)
h.ServeHTTP(w,r)
}

mux的ServeHTTP方法通过调用其Handler方法寻找注册到路由上的handler函数,并调用函数的ServeHTTP方法,本例则是IndexHandler函数。 mux的Handler方法对URL简单的处理,然后调用handler方法,后者会创建一个锁,同时调用match方法返回一个handler和pattern。 在match方法中,mux的m字段是函数,因此通过迭代m寻找出注册路由的patten模式与实际url匹配的handler函数并返回。 返回的结构一直传递到mux的ServeHTTP方法,接下来调用handler函数的ServeHTTP方法,即IndexHandler函数,然后把response写到http.RequestWirter对象返回给客户端。 上述函数运行结束即`serverHandler{c.server}.ServeHTTP(w,w.req)`运行结束。接下来就是对请求处理完毕之后上希望和连接断开的相关逻辑。 至此,Golang中一个完整的http服务介绍完毕,包括注册路由,开启监听,处理连接,路由处理函数

总结

多数的web应用基于HTTP协议,客户端和服务器通过request-response的方式交互。一个server并不可少的两部分莫过于路由注册和连接处理。Golang通过一个ServeMux实现了的multiplexer路由多路复用器来管理路由。同时提供一个Handler接口提供ServeHTTP用来实现handler处理其函数,后者可以处理实际request并构造response。 ServeMux和handler处理器函数的连接桥梁就是Handler接口。ServeMux的ServeHTTP方法实现了寻找注册路由的handler的函数,并调用该handler的ServeHTTP方法。ServeHTTP方法就是真正处理请求和构造响应的地方。 回顾go的http包实现http服务的流程,可见大师们的编码设计之功力。学习有利提高自身的代码逻辑组织能力。更好的学习方式除了阅读,就是实践,接下来,我们将着重讨论来构建http服务。尤其是构建http中间件函数

四、HTTP客户端工具

net/http不仅提供了服务端处理,还提供了客户端处理功能

http包中提供了Get、Post、Head、PostForm方法实现HTTP请求:

func Get(url ) (resp *<span style="color: #008000;">//<span style="color: #008000;">POST
func Post(url <span style="color: #0000ff;">string
,contentType <span style="color: #0000ff;">string
,body io.Reader) (resp *<span style="color: #000000;">Response,err error) {
<span style="color: #0000ff;">return<span style="color: #000000;"> DefaultClient.Post(url,contentType,body)
}

<span style="color: #008000;">//<span style="color: #008000;">HEAD
func Head(url <span style="color: #0000ff;">string) (resp *<span style="color: #000000;">Response,err error) {
<span style="color: #0000ff;">return<span style="color: #000000;"> DefaultClient.Head(url)
}

<span style="color: #008000;">//<span style="color: #008000;">POSTFORM
<span style="color: #000000;">
func PostForm(url <span style="color: #0000ff;">string,data url.Values) (resp *<span style="color: #000000;">Response,err error) {
<span style="color: #0000ff;">return<span style="color: #000000;"> DefaultClient.PostForm(url,data)
}

GET请求示例

import (
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">net/http<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">log<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">reflect<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">bytes<span style="color: #800000;">"<span style="color: #000000;">
)

func main() {

resp,err :</span>= http.Get(<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;http://www.baidu.com</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;)
</span><span style="color: #0000ff;"&gt;if</span> err !=<span style="color: #000000;"&gt; nil {
    </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; <a href="/tag/cuowu/" target="_blank" class="keywords">错误</a>处理</span>

<span style="color: #000000;"> log.Println(err)
<span style="color: #0000ff;">return<span style="color: #000000;">
}

defer resp.Body.Close() <span style="color: #008000;"&gt;//<a href="/tag/guanbi/" target="_blank" class="keywords">关闭</a><a href="/tag/lianjie/" target="_blank" class="keywords">链接</a></span>

headers :</span>=<span style="color: #000000;"&gt; resp.Header

</span><span style="color: #0000ff;"&gt;for</span> k,v :=<span style="color: #000000;"&gt; range headers {
    fmt.Printf(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;k=%v,v=%v\n</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;,k,v) <span style="color: #008000;"&gt;//所有头信息</span>
}

fmt.Printf(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;resp status %s,statusCode %d\n</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;,resp.Status,resp.StatusCode)

fmt.Printf(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;resp Proto %s\n</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;,resp.Proto)

fmt.Printf(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;resp content length %d\n</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;,resp.ContentLength)

fmt.Printf(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;resp transfer encoding %v\n</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;,resp.TransferEncoding)

fmt.Printf(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;resp Uncompressed %t\n</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;,resp.Uncompressed)

fmt.Println(reflect.TypeOf(resp.Body)) </span>
buf := bytes.NewBuffer(make([]<span style="color: #0000ff;"&gt;byte</span>,<span style="color: #800080;"&gt;0</span>,<span style="color: #800080;"&gt;512</span><span style="color: #000000;"&gt;))
length,_ :</span>=<span style="color: #000000;"&gt; buf.ReadFrom(resp.Body)
fmt.Println(len(buf.Bytes()))
fmt.Println(length)
fmt.Println(</span><span style="color: #0000ff;"&gt;string</span><span style="color: #000000;"&gt;(buf.Bytes()))

}

使用http.Do设置请求头、cookie等

import (
<span style="color: #800000;">"<span style="color: #800000;">net/http<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">strings<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">io/IoUtil<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">log<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"<span style="color: #000000;">
)

func main() {
client := &<span style="color: #000000;">http.Client{}
req,err := http.NewRequest(<span style="color: #800000;">"<span style="color: #800000;">POST<span style="color: #800000;">",<span style="color: #800000;">"<span style="color: #800000;">http://www.baidu.com<span style="color: #800000;">"<span style="color: #000000;">,strings.NewReader(<span style="color: #800000;">"<span style="color: #800000;">name=xxxx&passwd=xxxx<span style="color: #800000;">"<span style="color: #000000;">))
<span style="color: #0000ff;">if err !=<span style="color: #000000;"> nil {
fmt.Println(err)
<span style="color: #0000ff;">return<span style="color: #000000;">
}

req.Header.Set(</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;Content-Type</span><span style="color: #800000;"&gt;"</span>,<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;application/x-www-form-urlencoded; charset=UTF-8</span><span style="color: #800000;"&gt;"</span>) <span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt;设置请求头信息</span>

<span style="color: #000000;">
resp,err :=<span style="color: #000000;"> client.Do(req)

defer resp.Body.Close()

body,err :</span>=<span style="color: #000000;"&gt; <a href="/tag/IoU/" target="_blank" class="keywords">IoU</a>til.ReadAll(resp.Body)
</span><span style="color: #0000ff;"&gt;if</span> err !=<span style="color: #000000;"&gt; nil {
    log.Println(err)
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt;
}
</span><span style="color: #0000ff;"&gt;var</span> res <span style="color: #0000ff;"&gt;string</span><span style="color: #000000;"&gt;
res </span>= <span style="color: #0000ff;"&gt;string</span><span style="color: #000000;"&gt;(body[:])
fmt.Println(res)

}

POST请求示例

import (
<span style="color: #800000;">"<span style="color: #800000;">net/http<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">strings<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">io/IoUtil<span style="color: #800000;">"<span style="color: #000000;">
)

func main() {
resp,err := http.Post(<span style="color: #800000;">"<span style="color: #800000;">http://www.baidu.com<span style="color: #800000;">"<span style="color: #000000;">,<span style="color: #800000;">"<span style="color: #800000;">application/x-www-form-urlencoded<span style="color: #800000;">"<span style="color: #000000;">,strings.NewReader(<span style="color: #800000;">"<span style="color: #800000;">username=xxx&password=xxxx<span style="color: #800000;">"<span style="color: #000000;">))
<span style="color: #0000ff;">if err !=<span style="color: #000000;"> nil {
fmt.Println(err)
<span style="color: #0000ff;">return<span style="color: #000000;">
}

defer resp.Body.Close()
body,err :</span>=<span style="color: #000000;"&gt; <a href="/tag/IoU/" target="_blank" class="keywords">IoU</a>til.ReadAll(resp.Body)
</span><span style="color: #0000ff;"&gt;if</span> err !=<span style="color: #000000;"&gt; nil {
    fmt.Println(err)
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt;
}

fmt.Println(</span><span style="color: #0000ff;"&gt;string</span><span style="color: #000000;"&gt;(body))

}

PostForm请求示例

import (
<span style="color: #800000;">"<span style="color: #800000;">net/http<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">io/IoUtil<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">net/url<span style="color: #800000;">"<span style="color: #000000;">
)

func main() {

postParam :</span>=<span style="color: #000000;"&gt; url.Values{
    </span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;name</span><span style="color: #800000;"&gt;"</span>:      {<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;wd</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;},</span><span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;password</span><span style="color: #800000;"&gt;"</span>: {<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;1234</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;},}

resp,err :</span>= http.PostForm(<span style="color: #800000;"&gt;"</span><span style="color: #800000;"&gt;https://cn.bing.com/</span><span style="color: #800000;"&gt;"</span><span style="color: #000000;"&gt;,postParam)
</span><span style="color: #0000ff;"&gt;if</span> err !=<span style="color: #000000;"&gt; nil {
    fmt.Println(err)
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt;
}

defer resp.Body.Close()
body,err :</span>=<span style="color: #000000;"&gt; <a href="/tag/IoU/" target="_blank" class="keywords">IoU</a>til.ReadAll(resp.Body)
</span><span style="color: #0000ff;"&gt;if</span> err !=<span style="color: #000000;"&gt; nil {
    fmt.Println(err)
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt;
}

fmt.Println(</span><span style="color: #0000ff;"&gt;string</span><span style="color: #000000;"&gt;(body))

}

 

 

猜你在找的Go相关文章