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编程实现起来非常方便,下面是处理流程
服务器端:
- 监听端口
- 接受客户端连接
- 创建goroutine处理连接
客户端:
- 建立连接
- 收发数据
- 关闭连接
服务端示例:
package main import (
"fmt"
"net"
) func handle(conn net.Conn) { //处理连接方法
defer conn.Close() //关闭连接
for{
buf := make([]byte,)
n,err := conn.Read(buf) //读取客户端数据
if err!=nil {
fmt.Println(err)
return }
fmt.Printf("read data size %d msg:%s", n, string(buf[: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,)
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
接口。
对象说明:
- hander函数: 具有
func(w http.ResponseWriter, r *http.Requests)
签名的函数 - handler函数: 经过
HandlerFunc
结构包装的handler函数
,它实现了ServeHTTP接口方法的函数。调用handler处理器的ServeHTTP方法时,即调用handler函数本身。 - 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 == {
tempDelay = * time.Millisecond
} else {
tempDelay *=
}
if max := * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay =
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 = <<
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 != {
c.rwc.SetReadDeadline(time.Now().Add(d))
}
if d := c.server.WriteTimeout; d != {
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}, <<) 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(, ) && req.ContentLength != {
// 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() > {
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 != {
c.rwc.SetReadDeadline(time.Now().Add(d))
if _, err := c.bufr.Peek(); 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 =
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(, ) {
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服务介绍完毕,包括注册路由,开启监听,处理连接,路由处理函数。
总结
四、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, , ))
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": {""},
} 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处理流程详情的更多相关文章
- GitHub的实现是否是基于此语言的支持网络编程性呢?
我觉好像是的,我之前很奇怪为什么那样就可以引用了,后来发现GitHub中的java JavaScript phton等语言都支持网络编程.
- Go语言基础之网络编程
现在我们几乎每天都在使用互联网,我们前面已经学习了如何编写Go语言程序,但是如何才能让我们的程序通过网络互相通信呢?本章我们就一起来学习下Go语言中的网络编程. 关于网络编程其实是一个很庞大的领域,本 ...
- GO学习-(19) Go语言基础之网络编程
Go语言基础之网络编程 现在我们几乎每天都在使用互联网,我们前面已经学习了如何编写Go语言程序,但是如何才能让我们的程序通过网络互相通信呢?本章我们就一起来学习下Go语言中的网络编程. 关于网络编程其 ...
- Go语言系列之网络编程
现在我们几乎每天都在使用互联网,我们前面已经学习了如何编写Go语言程序,但是如何才能让我们的程序通过网络互相通信呢?本章我们就一起来学习下Go语言中的网络编程. 关于网络编程其实是一个很庞大的领域,本 ...
- GO语言练习:网络编程 ICMP 示例
1.代码 2.编译及运行 1.Go语言网络编程:ICMP示例代码 icmptest.go package main import ( "fmt" "net" & ...
- Linux下C语言的socket网络编程
关于详细的服务器建立的步骤以及相关的socket套接字的知识我已经在python socket编程的文章中提到过了,大家可以参看那一篇博客来历接socket套接字编程的内容,由于要是用C相关的API所 ...
- GO语言练习:网络编程 TCP 示例
1.代码 2.编译及运行 1.网络编程 TCP 示例 simplehttp.go 代码 package main import ( "net" "os" &qu ...
- linux下的C语言开发(网络编程)
http://blog.csdn.net/feixiaoxing/article/details/7259675 [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing ...
- 基于C语言的Socket网络编程搭建简易的Web服务器(socket实现的内部原理)
首先编写我们服务器上需要的c文件WebServer.c 涉及到的函数API: int copy(FILE *read_f, FILE * write_f) ----- 文件内容复制的方法 int Do ...
随机推荐
- 平衡树、AVL树
平衡树 平衡树有AVL树.红黑树.2-3树.2-3-4树 AVL树 AVL树是最早的一种平衡树,它以发明者的名字命名:AVL是一种特殊的二叉搜索树,平移保证二叉搜索树的正确. 特征 在AVL树中节点的 ...
- HDFS hflush hsync和close的区别
HDFS的hflush,hsync和close有啥区别,分别做了什么 hflush: 语义是保证flush的数据被新的reader读到,但是不保证数据被datanode持久化. hsync: 与hfl ...
- 控制台输出 mybatis 中的sql语句
控制台输出 mybatis 中的sql语句 在 log4j.xml 文件中 增加如下配置 <!-- mybatis 输出的sql,DEBUG级别 --> <logger name=& ...
- python 之@staticmethod和@classmethod
在python中,要调用一个类中的方法,一般的操作步骤如下: 1.实例化此类 2.调用此类中的方法 而@staticmethod和@classmethod则打破了这种引用方式,可以在不实例化类的情况下 ...
- 解决Hsqldb指针只能单向移动,不能回滚问题(.first())
Class.forName("org.hsqldb.jdbcDriver").newInstance(); Connection con = java.sql.DriverMana ...
- USB 相关笔记
1分析已有代码项目 Android从USB声卡录制高质量音频-----使用libusb读取USB声卡数据 github 项目:usbaudio-android-demo usb声卡取数据项目也是参考的 ...
- orcl 复杂查询
测试环境: create table bqh6 (xm varchar2(10),bmbh number(2),bmmc varchar2(15),gz int);insert into bqh6 v ...
- vbs常用函数
aa '删除文件夹 sub DeleteFolder(objFolder) call OutputLog(objFolder.Path,true) err.Clear On Error Resume ...
- 用JS制作《飞机大作战》游戏_第4讲(创建敌方飞机、敌方飞机发射子弹、玩家子弹击中敌方小飞机,小飞机死亡)-陈远波
一.创建敌方飞机 1.思考创建思路: 创建敌方飞机思路与创建玩家飞机思路一样: (1)思考敌方飞机具备什么属性: 敌方飞机的图片.坐标.飞行速度.状态(是否被击中) 设置小飞机被击中时消失时间.飞机可 ...
- ubuntu环境下docker的安装与操作
只要按照本文的步骤一步步的走,就能正确的安装docker并使用,ubuntu需要联网 1. 在Ubuntu中安装Docker 更新ubuntu的apt源索引 sudo apt-get update 安 ...