Go Web 基础--原理总结

Web的工作方式

  对于普通的上网过程

  浏览器本身是一个客户端,当你输入URL的时候,首先浏 览器会去请求DNS服务器,通过DNS获取相应的域名对应的IP,然后通过IP地址找到IP对应的服务器 后,要求建立TCP连接,等浏览器发送完HTTP Request(请求)包后,服务器接收到请求包之后才开 始处理请求包,服务器调用自身服务,返回HTTP Response(响应)包;客户端收到来自服务器的响 应后开始渲染这个Response包里的主体(body),等收到全部的内容随后断开与该服务器之间的TCP 连接。

  Web服务器的工作原理

  客户端通过TCP/IP协议建立到服务器的链接

  客户端向服务器发送Http请求协议包,请求服务器里的资源

  服务器向客户机发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调 用动态语言的解释引擎负责处理动态内容,并将处理得到的数据返回给客户端

  客户机与服务器断开。由客户端解释HTML文档,在客户端屏幕上渲染图形结果

  HTTP协议

  HTTP是一种让Web服务器与浏览器(客户端)通过Internet发送与接收数据的协议,它建立在TCP协议 之上。它是一个请求、响应协议客户端发出一个请求,服务器响应这个请 求。在HTTP中,客户端总是通过建立一个连接与发送一个HTTP请求来发起一个事务。服务器不能主动 去与客户端联系,也不能给客户端发出一个回调连接。客户端与服务器端都可以提前中断一个连接

  Get与Post的区别

  Get请求消息体为空,Post请求带有消息体

  Get提交的数据会放在URL后,Post是将数据放在Http包的body中

  GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制。

  GET方式提交数据,会带来安全问题,比如一个登录页面,通过GET方式提交数据时,用户名和密 码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该 用户的账号和密码。

基于Go语言创建一个服务器

  示例代码中的关键字可以参考之前写的随笔!!!

package main

import (
    "fmt"
    "net/http"
    "strings"
)

func GetInitInfo(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    fmt.Println(r.Form)
    fmt.Println(r.URL.Path)
    fmt.Println(r.URL.Scheme)
    for k, v := range r.Form {
        fmt.Println("key:", k)
        fmt.Println("val:", strings.Join(v, ""))
    }
    fmt.Fprintf(w, "this is init information")
}

func main() {
    http.HandleFunc("/init", GetInitInfo)
    err := http.ListenAndServe("localhost:8080", nil)
    if err != nil {
        panic(err)
    }
    
}

结果:

 服务端几个必备的概念

  Request:用户请求的信息,用来解析用户请求的信息,包括Post,Get,Cookie,Url等信息

  Response:服务器需要反馈给客户端的信息

  Conn:用户的每次请求链接

  Handler:处理请求和生成返回信息的处理逻辑

分析Http包的运行机制

  1.创建Listen Socket,监听指定的端口,等待客户端请求到来

  2.Listen Socket 接收客户端的请求,得到Client Socket,接下来通过Client Socket与客户端通信

  3.处理客户端的请求, 首先从Client Socket读取HTTP请求的协议头, 如果是POST方法, 还可 能要读取客户端提交的数据, 然后交给相应的handler处理请求, handler处理完毕准备好客户 端需要的数据, 通过Client Socket写给客户端。

三个值得探究的问题

  1.如何监听端口?

  2.如何接收客户端请求?

  3.如何分配handler?

  http包中的一段源码

func (srv *Server) Serve(l net.Listener) error {
	if fn := testHookServerServe; fn != nil {
		fn(srv, l) // call hook with unwrapped listener
	}

	origListener := l
	l = &onceCloseListener{Listener: l}
	defer l.Close()

	if err := srv.setupHTTP2_Serve(); err != nil {
		return err
	}

	if !srv.trackListener(&l, true) {
		return ErrServerClosed
	}
	defer srv.trackListener(&l, false)

	baseCtx := context.Background()
	if srv.BaseContext != nil {
		baseCtx = srv.BaseContext(origListener)
		if baseCtx == nil {
			panic("BaseContext returned a nil context")
		}
	}

	var tempDelay time.Duration // how long to sleep on accept failure

	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
	for {
		rw, err := l.Accept()
		if err != nil {
			select {
			case <-srv.getDoneChan():
				return ErrServerClosed
			default:
			}
			if ne, ok := err.(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", err, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return err
		}
		connCtx := ctx
		if cc := srv.ConnContext; cc != nil {
			connCtx = cc(connCtx, rw)
			if connCtx == nil {
				panic("ConnContext returned nil")
			}
		}
		tempDelay = 0
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew, runHooks) // before Serve can return
		go c.serve(connCtx)
	}
}

  

  Go是通过一个函数 ListenAndServe 来处理这些事情的,这个底层其实这样处理的:初始化一个server对象,然后调用了 net.Listen("tcp", addr) ,也就是底层 用TCP协议搭建了一个服务,然后监控我们设置的端口。

  调用了 srv.Serve(net.Listener) 函数,这个函数就是处理接收客户端的请求信息。这个函数里面起了 一个 for{} ,首先通过Listener接收请求,其次创建一个Conn,最后单独开了一个goroutine, 把这个请求的数据当做参数扔给这个conn去服务: go c.serve() 。这个就是高并发体现了,用户 的每一次请求都是在一个新的goroutine去服务,相互不影响。

  conn首先会解析request: c.readRequest() ,然后 获取相应的handler: handler := c.server.Handler ,也就是我们刚才在调用函ListenAndServe 时候的第二个参数,我们前面例子传递的是nil,也就是为空,那么默认获取 handler = DefaultServeMux ,那么这个变量用来做什么的呢?对,这个变量就是一个路由器, 它用来匹配url跳转到其相应的handle函数,那么这个我们有设置过吗?有,我们调用的代码里面第一 句不是调用了 http.HandleFunc("/", GetInitInfo) 嘛。这个作用就是注册了请求 / 的路由规 则,当请求uri”/init“,路由就会转到函数GetInitInfoDefaultServeMux会调用ServeHTTP方 法,这个方法内部其实就是调用GetInitInfo本身,最后通过写入response的信息反馈到客户端。

 

 

 

 

 

 

 

 

 

 

 

 

    

posted @ 2023-05-07 22:18  99号的格调  阅读(46)  评论(0编辑  收藏  举报