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

互联网 19-11-29

一、简介

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、关闭连接

服务端示例:

package main  import (     "fmt"     "net" )  func handle(conn net.Conn)  {    //处理连接方法     defer conn.Close()  //关闭连接     for{         buf := make([]byte,100)         n,err := conn.Read(buf)  //读取客户端数据         if err!=nil {             fmt.Println(err)             return          }         fmt.Printf("read data size %d msg:%s", n, string(buf[0:n]))         msg := []byte("hello,world\n")         conn.Write(msg)  //发送数据     } } func main()  {     fmt.Println("start server....")     listen,err := net.Listen("tcp","0.0.0.0:3000") //创建监听     if err != nil{         fmt.Println("listen failed! msg :" ,err)         return     }     for{         conn,errs := listen.Accept()  //接受客户端连接         if errs != nil{             fmt.Println("accept failed")             continue         }         go handle(conn) //处理连接     } }

客户端示例:

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

conn示例还提供其他方法:

type Conn interface {     // Read reads data from the connection.     // Read can be made to time out and return an Error with Timeout() == true     // after a fixed time limit; see SetDeadline and SetReadDeadline.     Read(b []byte) (n int, err error)  //读取连接中数据          // Write writes data to the connection.     // Write can be made to time out and return an Error with Timeout() == true     // after a fixed time limit; see SetDeadline and SetWriteDeadline.     Write(b []byte) (n int, err error) //发送数据      // Close closes the connection.     // Any blocked Read or Write operations will be unblocked and return errors.     Close() error   //关闭链接      // LocalAddr returns the local network address.     LocalAddr() Addr //返回本地连接地址      // RemoteAddr returns the remote network address.     RemoteAddr() Addr //返回远程连接的地址      // SetDeadline sets the read and write deadlines associated     // with the connection. It is equivalent to calling both     // SetReadDeadline and SetWriteDeadline.     //     // A deadline is an absolute time after which I/O operations     // fail with a timeout (see type Error) instead of     // blocking. The deadline applies to all future and pending     // I/O, not just the immediately following call to Read or     // Write. After a deadline has been exceeded, the connection     // can be refreshed by setting a deadline in the future.     //     // An idle timeout can be implemented by repeatedly extending     // the deadline after successful Read or Write calls.     //     // A zero value for t means I/O operations will not time out.     SetDeadline(t time.Time) error //设置链接读取或者写超时时间      // SetReadDeadline sets the deadline for future Read calls     // and any currently-blocked Read call.     // A zero value for t means Read will not time out.     SetReadDeadline(t time.Time) error //单独设置读取超时时间      // SetWriteDeadline sets the deadline for future Write calls     // and any currently-blocked Write call.     // Even if write times out, it may return n > 0, indicating that     // some of the data was successfully written.     // A zero value for t means Write will not time out.     SetWriteDeadline(t time.Time) error//单独设置写超时时间 }

三、go中HTTP服务处理流程

简介

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

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

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

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

对象说明:

1、hander函数: 具有func(w http.ResponseWriter, r *http.Requests)签名的函数

2、handler函数: 经过HandlerFunc结构包装的handler函数,它实现了ServeHTTP接口方法的函数。调用handler处理器的ServeHTTP方法时,即调用handler函数本身。

3、handler对象:实现了Handler接口ServeHTTP方法的结构。

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

Handler

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

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

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

ServeMux

源码部分:

type ServeMux struct {     mu    sync.RWMutex     m     map[string]muxEntry     hosts bool  }  type muxEntry struct {     explicit bool     h        Handler     pattern  string }

ServeMux结构中最重要的字段为m,这是一个map,key是一些url模式,value是一个muxEntry结构,后者里定义存储了具体的url模式和handler。

当然,所谓的ServeMux也实现了ServeHTTP接口,也算是一个handler,不过ServeMux的ServeHTTP方法不是用来处理request和respone,而是用来找到路由注册的handler,后面再做解释。

Server

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

func ListenAndServe(addr string, handler Handler) error {     server := &Server{Addr: addr, Handler: handler}         return server.ListenAndServe() }

查看server的结构如下:

type Server struct {     Addr         string             Handler      Handler            ReadTimeout  time.Duration      WriteTimeout time.Duration      TLSConfig    *tls.Config         MaxHeaderBytes int      TLSNextProto map[string]func(*Server, *tls.Conn, Handler)      ConnState func(net.Conn, ConnState)     ErrorLog *log.Logger     disableKeepAlives int32     nextProtoOnce     sync.Once      nextProtoErr      error      }

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

创建HTTP服务

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

http.HandleFunc("/", indexHandler) http.ListenAndServe("127.0.0.1:8000", nil) 或 server := &Server{Addr: addr, Handler: handler}  server.ListenAndServe()

示例:

package main import ( "fmt" "net/http" )  func Hello(w http.ResponseWriter, r *http.Request) { fmt.Println("Hello World.") fmt.Fprintf(w, "Hello World.\n") }  func main() { http.HandleFunc("/", Hello) err := http.ListenAndServe("0.0.0.0:6000", nil) if err != nil { fmt.Println("http listen failed.") } }  //curl http://127.0.0.1:6000   // 结果:Hello World

路由注册

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

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {     DefaultServeMux.HandleFunc(pattern, handler) }

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

// NewServeMux allocates and returns a new ServeMux. func NewServeMux() *ServeMux { return new(ServeMux) }  // DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = &defaultServeMux  var defaultServeMux ServeMux

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

// HandleFunc registers the handler function for the given pattern.func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {     mux.Handle(pattern, HandlerFunc(handler)) }

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

type HandlerFunc func(ResponseWriter, *Request)  // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {     f(w, r) }

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

func ListenAndServe(addr string, handler Handler) error {     server := &Server{Addr: addr, Handler: handler}     return server.ListenAndServe() } // ListenAndServe listens on the TCP network address srv.Addr and then // calls Serve to handle requests on incoming connections. // Accepted connections are configured to enable TCP keep-alives. // If srv.Addr is blank, ":http" is used. // ListenAndServe always returns a non-nil error. func (srv *Server) ListenAndServe() error {     addr := srv.Addr     if addr == "" {         addr = ":http"     }     ln, err := net.Listen("tcp", addr)     if err != nil {         return err     }     return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) }

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

// Serve accepts incoming connections on the Listener l, creating a // new service goroutine for each. The service goroutines read requests and // then call srv.Handler to reply to them. // // For HTTP/2 support, srv.TLSConfig should be initialized to the // provided listener's TLS Config before calling Serve. If // srv.TLSConfig is non-nil and doesn't include the string "h2" in // Config.NextProtos, HTTP/2 support is not enabled. // // Serve always returns a non-nil error. After Shutdown or Close, the // returned error is ErrServerClosed. func (srv *Server) Serve(l net.Listener) error {     defer l.Close()     if fn := testHookServerServe; fn != nil {         fn(srv, l)     }     var tempDelay time.Duration // how long to sleep on accept failure      if err := srv.setupHTTP2_Serve(); err != nil {         return err     }      srv.trackListener(l, true)     defer srv.trackListener(l, false)      baseCtx := context.Background() // base is always background, per Issue 16220     ctx := context.WithValue(baseCtx, ServerContextKey, srv)     for {         rw, e := l.Accept()         if e != nil {             select {             case <-srv.getDoneChan():                 return ErrServerClosed             default:             }             if ne, ok := e.(net.Error); ok && ne.Temporary() {                 if tempDelay == 0 {                     tempDelay = 5 * time.Millisecond                 } else {                     tempDelay *= 2                 }                 if max := 1 * time.Second; tempDelay > max {                     tempDelay = max                 }                 srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)                 time.Sleep(tempDelay)                 continue             }             return e         }         tempDelay = 0         c := srv.newConn(rw)         c.setState(c.rwc, StateNew) // before Serve can return         go c.serve(ctx)     } }

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

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

// Serve a new connection. func (c *conn) serve(ctx context.Context) {     c.remoteAddr = c.rwc.RemoteAddr().String()     ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())     defer func() {         if err := recover(); err != nil && err != ErrAbortHandler {             const size = 64 << 10             buf := make([]byte, size)             buf = buf[:runtime.Stack(buf, false)]             c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)         }         if !c.hijacked() {             c.close()             c.setState(c.rwc, StateClosed)         }     }()      if tlsConn, ok := c.rwc.(*tls.Conn); ok {         if d := c.server.ReadTimeout; d != 0 {             c.rwc.SetReadDeadline(time.Now().Add(d))         }         if d := c.server.WriteTimeout; d != 0 {             c.rwc.SetWriteDeadline(time.Now().Add(d))         }         if err := tlsConn.Handshake(); err != nil {             c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)             return         }         c.tlsState = new(tls.ConnectionState)         *c.tlsState = tlsConn.ConnectionState()         if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {             if fn := c.server.TLSNextProto[proto]; fn != nil {                 h := initNPNRequest{tlsConn, serverHandler{c.server}}                 fn(c.server, tlsConn, h)             }             return         }     }      // HTTP/1.x from here on.      ctx, cancelCtx := context.WithCancel(ctx)     c.cancelCtx = cancelCtx     defer cancelCtx()      c.r = &connReader{conn: c}     c.bufr = newBufioReader(c.r)     c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)      for {         w, err := c.readRequest(ctx)         if c.r.remain != c.server.initialReadLimitSize() {             // If we read any bytes off the wire, we're active.             c.setState(c.rwc, StateActive)         }         if err != nil {             const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"              if err == errTooLarge {                 // Their HTTP client may or may not be                 // able to read this if we're                 // responding to them and hanging up                 // while they're still writing their                 // request. Undefined behavior.                 const publicErr = "431 Request Header Fields Too Large"                 fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)                 c.closeWriteAndWait()                 return             }             if isCommonNetReadError(err) {                 return // don't reply             }              publicErr := "400 Bad Request"             if v, ok := err.(badRequestError); ok {                 publicErr = publicErr + ": " + string(v)             }              fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)             return         }          // Expect 100 Continue support         req := w.req         if req.expectsContinue() {             if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {                 // Wrap the Body reader with one that replies on the connection                 req.Body = &expectContinueReader{readCloser: req.Body, resp: w}             }         } else if req.Header.get("Expect") != "" {             w.sendExpectationFailed()             return         }          c.curReq.Store(w)          if requestBodyRemains(req.Body) {             registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)         } else {             if w.conn.bufr.Buffered() > 0 {                 w.conn.r.closeNotifyFromPipelinedRequest()             }             w.conn.r.startBackgroundRead()         }          // HTTP cannot have multiple simultaneous active requests.[*]         // Until the server replies to this request, it can't read another,         // so we might as well run the handler in this goroutine.         // [*] Not strictly true: HTTP pipelining. We could let them all process         // in parallel even if their responses need to be serialized.         // But we're not going to implement HTTP pipelining because it         // was never deployed in the wild and the answer is HTTP/2.         serverHandler{c.server}.ServeHTTP(w, w.req)         w.cancelCtx()         if c.hijacked() {             return         }         w.finishRequest()         if !w.shouldReuseConnection() {             if w.requestBodyLimitHit || w.closedRequestBodyEarly() {                 c.closeWriteAndWait()             }             return         }         c.setState(c.rwc, StateIdle)         c.curReq.Store((*response)(nil))          if !w.conn.server.doKeepAlives() {             // We're in shutdown mode. We might've replied             // to the user without "Connection: close" and             // they might think they can send another             // request, but such is life with HTTP/1.1.             return         }          if d := c.server.idleTimeout(); d != 0 {             c.rwc.SetReadDeadline(time.Now().Add(d))             if _, err := c.bufr.Peek(4); err != nil {                 return             }         }         c.rwc.SetReadDeadline(time.Time{})     } }  serve方法

使用defer定义了函数退出时,连接关闭相关的处理。然后就是读取连接的网络数据,并处理读取完毕时候的状态。接下来就是调用serverHandler{c.server}.ServeHTTP(w, w.req)方法处理请求了。最后就是请求处理完毕的逻辑。

serverHandler是一个重要的结构,它近有一个字段,即Server结构,同时它也实现了Handler接口方法ServeHTTP,并在该接口方法中做了一个重要的事情,初始化multiplexer路由多路复用器。

如果server对象没有指定Handler,则使用默认的DefaultServeMux作为路由Multiplexer。并调用初始化Handler的ServeHTTP方法。

// serverHandler delegates to either the server's Handler or // DefaultServeMux and also handles "OPTIONS *" requests. type serverHandler struct {     srv *Server }  func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {     handler := sh.srv.Handler     if handler == nil {         handler = DefaultServeMux     }     if req.RequestURI == "*" && req.Method == "OPTIONS" {         handler = globalOptionsHandler{}     }     handler.ServeHTTP(rw, req) }

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

// Find a handler on a handler map given a path string. // Most-specific (longest) pattern wins. func (mux *ServeMux) match(path string) (h Handler, pattern string) {     // Check for exact match first.     v, ok := mux.m[path]     if ok {         return v.h, v.pattern     }      // Check for longest valid match.     var n = 0     for k, v := range mux.m {         if !pathMatch(k, path) {             continue         }         if h == nil || len(k) > n {             n = len(k)             h = v.h             pattern = v.pattern         }     }     return } func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {      // CONNECT requests are not canonicalized.     if r.Method == "CONNECT" {         return mux.handler(r.Host, r.URL.Path)     }      // All other requests have any port stripped and path cleaned     // before passing to mux.handler.     host := stripHostPort(r.Host)     path := cleanPath(r.URL.Path)     if path != r.URL.Path {         _, pattern = mux.handler(host, path)         url := *r.URL         url.Path = path         return RedirectHandler(url.String(), StatusMovedPermanently), pattern     }      return mux.handler(host, r.URL.Path) }  // handler is the main implementation of Handler. // The path is known to be in canonical form, except for CONNECT methods. func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {     mux.mu.RLock()     defer mux.mu.RUnlock()      // Host-specific pattern takes precedence over generic ones     if mux.hosts {         h, pattern = mux.match(host + path)     }     if h == nil {         h, pattern = mux.match(path)     }     if h == nil {         h, pattern = NotFoundHandler(), ""     }     return }  // ServeHTTP dispatches the request to the handler whose // pattern most closely matches the request URL. func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {     if r.RequestURI == "*" {         if r.ProtoAtLeast(1, 1) {             w.Header().Set("Connection", "close")         }         w.WriteHeader(StatusBadRequest)         return     }     h, _ := mux.Handler(r)     h.ServeHTTP(w, r) }

mux的ServeHTTP方法通过调用其Handler方法寻找注册到路由上的handler函数,并调用该函数的ServeHTTP方法,本例则是IndexHandler函数。

mux的Handler方法对URL简单的处理,然后调用handler方法,后者会创建一个锁,同时调用match方法返回一个handler和pattern。 在match方法中,mux的m字段是map[string]muxEntry图,后者存储了pattern和handler处理器函数,因此通过迭代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请求:

//GET func Get(url string) (resp *Response, err error) {     return DefaultClient.Get(url) }  //POST func Post(url string, contentType string, body io.Reader) (resp *Response, err error) {     return DefaultClient.Post(url, contentType, body) }  //HEAD func Head(url string) (resp *Response, err error) {     return DefaultClient.Head(url) }  //POSTFORM  func PostForm(url string, data url.Values) (resp *Response, err error) {     return DefaultClient.PostForm(url, data) }

GET请求示例

package main  import (     "fmt"     "net/http"     "log"     "reflect"     "bytes" )  func main() {      resp, err := http.Get("http://www.baidu.com")     if err != nil {         // 错误处理         log.Println(err)         return     }      defer resp.Body.Close() //关闭链接      headers := resp.Header      for k, v := range headers {         fmt.Printf("k=%v, v=%v\n", k, v) //所有头信息     }      fmt.Printf("resp status %s,statusCode %d\n", resp.Status, resp.StatusCode)      fmt.Printf("resp Proto %s\n", resp.Proto)      fmt.Printf("resp content length %d\n", resp.ContentLength)      fmt.Printf("resp transfer encoding %v\n", resp.TransferEncoding)      fmt.Printf("resp Uncompressed %t\n", resp.Uncompressed)      fmt.Println(reflect.TypeOf(resp.Body))      buf := bytes.NewBuffer(make([]byte, 0, 512))     length, _ := buf.ReadFrom(resp.Body)     fmt.Println(len(buf.Bytes()))     fmt.Println(length)     fmt.Println(string(buf.Bytes())) }

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

package main  import (     "net/http"     "strings"     "io/ioutil"     "log"     "fmt" )  func main() {     client := &http.Client{}     req, err := http.NewRequest("POST", "http://www.baidu.com",         strings.NewReader("name=xxxx&passwd=xxxx"))     if err != nil {         fmt.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     }     var res string     res = string(body[:])     fmt.Println(res) }

POST请求示例

package main  import (     "net/http"     "strings"     "fmt"     "io/ioutil" )  func main() {     resp, err := http.Post("http://www.baidu.com",         "application/x-www-form-urlencoded",         strings.NewReader("username=xxx&password=xxxx"))     if err != nil {         fmt.Println(err)         return     }      defer resp.Body.Close()     body, err := ioutil.ReadAll(resp.Body)     if err != nil {         fmt.Println(err)         return     }      fmt.Println(string(body)) }

PostForm请求示例

package main  import (     "net/http"     "fmt"     "io/ioutil"     "net/url" )  func main() {      postParam := url.Values{         "name":      {"wd"},         "password": {"1234"},     }      resp, err := http.PostForm("https://cn.bing.com/", postParam)     if err != nil {         fmt.Println(err)         return     }      defer resp.Body.Close()     body, err := ioutil.ReadAll(resp.Body)     if err != nil {         fmt.Println(err)         return     }      fmt.Println(string(body)) }

以上就是go语言网络编程、http处理流程详情的详细内容,更多内容请关注技术你好其它相关文章!

来源链接:
免责声明:
1.资讯内容不构成投资建议,投资者应独立决策并自行承担风险
2.本文版权归属原作所有,仅代表作者本人观点,不代表本站的观点或立场
标签: go语言
上一篇:php获取远程图片并下载保存到本地的方法分析 下一篇:Go安装各种第三方库、包的命令

相关资讯