我对Context和Handler是最迷惑的,什么玩意儿,用他就实现通信啦??
没有直观理解,不知道到底扮演一个什么角色。

1、Context

上下文

Context深度理解
每一段程序都有很多外部变量(极度简单的函数略过),一旦有了外部变量,这段程序就不完整,不能独立运行,而为了让他们能运行,就要给所有的外部变量设置值,而这些值的集合就叫做Context

例1:标准库中的Context

1 可以从里面获取session等对象,是个容器。【这个里面好像不能】
2 准确说是个map,里面装满了key-value,调用Value(key)函数可以获取key对应的value。
3 有个Done通道,可用于废除

import "context"

以go的源码为例,import "context"包下的context.go-Context接口:

点击查看代码
// A Context carries a deadline, a cancellation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
	// Deadline returns the time when work done on behalf of this context
	// should be canceled. Deadline returns ok==false when no deadline is
	// set. Successive calls to Deadline return the same results.
	Deadline() (deadline time.Time, ok bool)

	// Done returns a channel that's closed when work done on behalf of this
	// context should be canceled. Done may return nil if this context can
	// never be canceled. Successive calls to Done return the same value.
	// The close of the Done channel may happen asynchronously,
	// after the cancel function returns.
	//
	// WithCancel arranges for Done to be closed when cancel is called;
	// WithDeadline arranges for Done to be closed when the deadline
	// expires; WithTimeout arranges for Done to be closed when the timeout
	// elapses.
	//
	// Done is provided for use in select statements:
	//
	//  // Stream generates values with DoSomething and sends them to out
	//  // until DoSomething returns an error or ctx.Done is closed.
	//  func Stream(ctx context.Context, out chan<- Value) error {
	//  	for {
	//  		v, err := DoSomething(ctx)
	//  		if err != nil {
	//  			return err
	//  		}
	//  		select {
	//  		case <-ctx.Done():
	//  			return ctx.Err()
	//  		case out <- v:
	//  		}
	//  	}
	//  }
	//
	// See https://blog.golang.org/pipelines for more examples of how to use
	// a Done channel for cancellation.
	Done() <-chan struct{} // Done是函数名,<-chan struct{}是返回值,表示返回一个struct{}类型的channel

	// If Done is not yet closed, Err returns nil.
	// If Done is closed, Err returns a non-nil error explaining why:
	// Canceled if the context was canceled
	// or DeadlineExceeded if the context's deadline passed.
	// After Err returns a non-nil error, successive calls to Err return the same error.
	Err() error

	// Value returns the value associated with this context for key, or nil
	// if no value is associated with key. Successive calls to Value with
	// the same key returns the same result.
	//
	// Use context values  使用context的value
	// only for request-scoped data that transits processes and API boundaries,    仅在request域数据中传递process和API边界时用context
	// not for passing optional parameters to functions.   在给函数传参时不用context
	//
	// A key identifies a specific value in a Context. 
	// Functions that wish to store values in Context typically allocate a key in a global
	// variable then use that key as the argument to context.WithValue and Context.Value. 
	// A key can be any type that supports equality;
	// packages should define keys as an unexported type to avoid collisions.
	//
	// Packages that define a Context key should provide type-safe accessors
	// for the values stored using that key:
	//
	// 	// Package user defines a User type that's stored in Contexts.
	// 	package user
	//
	// 	import "context"
	//
	// 	// User is the type of value stored in the Contexts.
	// 	type User struct {...}
	//
	// 	// key is an unexported type for keys defined in this package.
	// 	// This prevents collisions with keys defined in other packages.
	// 	type key int
	//
	// 	// userKey is the key for user.User values in Contexts. It is
	// 	// unexported; clients use user.NewContext and user.FromContext
	// 	// instead of using this key directly.
	// 	var userKey key
	//
	// 	// NewContext returns a new Context that carries value u.
	// 	func NewContext(ctx context.Context, u *User) context.Context {
	// 		return context.WithValue(ctx, userKey, u)
	// 	}
	//
	// 	// FromContext returns the User value stored in ctx, if any.
	// 	func FromContext(ctx context.Context) (*User, bool) {
	// 		u, ok := ctx.Value(userKey).(*User)
	// 		return u, ok
	// 	}
	Value(key any) any  【这是一个函数声明,函数名是Value 参数名是key,参数类型是any接口,返回类型是any接口】
}

例2:gin中的Context

import "github.com/gin-gonic/gin"

1 Context是gin最重要的部分
2 可以在中间件之间传递变量,管理flow,验证请求的JSON并呈现JSON响应
3 Keys是仅仅一次请求中的key/value对

context.go中的Context:

// Context is the most important part of gin. It allows us to pass variables between middleware,
// manage the flow, validate the JSON of a request and render a JSON response for example.
type Context struct {
	writermem responseWriter
	Request   *http.Request
	Writer    ResponseWriter

	Params   Params
	handlers HandlersChain
	index    int8
	fullPath string

	engine *Engine
	params *Params

	// This mutex protect Keys map
	mu sync.RWMutex

	// Keys is a key/value pair exclusively for the context of each request.
	Keys map[string]interface{}

	// Errors is a list of errors attached to all the handlers/middlewares who used this context.
	Errors errorMsgs

	// Accepted defines a list of manually accepted formats for content negotiation.
	Accepted []string

	// queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query()
	queryCache url.Values

	// formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH,
	// or PUT body parameters.
	formCache url.Values

	// SameSite allows a server to define a cookie attribute making it impossible for
	// the browser to send this cookie along with cross-site requests.
	sameSite http.SameSite
}

2、Socket编程

(1)Listen()创建服务器

The Listen function creates servers:

ln, err := net.Listen("tcp", ":8080")
if err != nil {
	// handle error
}
for {
	conn, err := ln.Accept()
	if err != nil {
		// handle error
	}
	go handleConnection(conn)
}

(2)Dial()连接服务器

Dial

The Dial function connects to a server:

conn, err := net.Dial("tcp", "golang.org:80")
if err != nil {
	// handle error
}
fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
status, err := bufio.NewReader(conn).ReadString('\n')
// ...

Go 语言中可以通过 Dial() 函数建立网络连接。

实际上,Dial() 函数是对 dialTCP()、dialUDP()、dialIP() 和 dialUnix() 的封装,这可以通过追溯 Dial() 函数的源码,调用链条Dial()-Dialer.Dial()-Dialer.DialContext()-sysDialer.dialSerial(),底层真正建立连接是通过 dialSingle() 函数完成的:

dialSingle() 函数通过从传入参数中获取网络协议类型调用对应的连接建立函数并返回连接对象。再往下追溯,可以看到这些底层函数最终都调用了 syscall 包的 Socket() 函数与对应平台操纵系统的 Socket API 交互实现网络连接的建立,针对不同的通信协议,建立不同的连接类型:

参考:
https://cloud.tencent.com/developer/article/1526910
https://golang.google.cn/pkg/net/

DialTCP

发起一个TCP连接

func net.DialTCP(network string, laddr *net.TCPAddr, raddr *net.TCPAddr) (*net.TCPConn, error)
// laddr is the local address,适用于多网卡的情况,nil时默认为localhost(一般是只有一个网卡)
// raddr is the remote address,远程地址即服务器地址

10、web服务器端的几个概念

Request:用户请求的信息,用来解析用户的请求信息,包括post、get、cookie、url等信息

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

Conn:用户的每次请求链接

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

参考:
https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/03.3.md

posted on 2023-01-31 14:29  西伯尔  阅读(30)  评论(0编辑  收藏  举报